Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e960b7d45 | ||
|
|
0c54d4a6a1 | ||
|
|
526e846a81 | ||
|
|
8bcfa373d4 | ||
|
|
db9a0f2c1f | ||
|
|
646b1e2193 | ||
|
|
a886166489 | ||
|
|
2073a9f3e5 | ||
|
|
be12049a87 | ||
|
|
de7dfc2ea6 | ||
|
|
7337a467b9 | ||
|
|
8118b4a774 | ||
|
|
b295743330 | ||
|
|
4f8d7092e0 | ||
|
|
2701e57779 | ||
|
|
f9375d5c33 | ||
|
|
d04f049fa3 | ||
|
|
8b8e724761 | ||
|
|
45e8c57f77 | ||
|
|
d01b3301d0 | ||
|
|
a7a55c7617 | ||
|
|
0695c12420 | ||
|
|
4af533c010 | ||
|
|
6a28d2b81c | ||
|
|
54769a2ad3 | ||
|
|
87cece8379 | ||
|
|
fe3790cb9c | ||
|
|
39cb238065 | ||
|
|
2182149b7a | ||
|
|
006e0548c1 | ||
|
|
8257fbdbf3 | ||
|
|
f57224952a | ||
|
|
3c982cae64 | ||
|
|
3ffe63c02c | ||
|
|
7aa5d4a375 | ||
|
|
1d06dd630a | ||
|
|
f630fb6732 |
+6
-6
@@ -35,16 +35,16 @@ set(FILAMENT_NDK_VERSION "" CACHE STRING
|
||||
"Android NDK version or version prefix to be used when building for Android."
|
||||
)
|
||||
|
||||
set(FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB "2" CACHE STRING
|
||||
"Per render pass arena size. Must be roughly 1 MB larger than FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB, default 2."
|
||||
set(FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB "3" CACHE STRING
|
||||
"Per render pass arena size. Must be roughly 1 MB larger than FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB, default 3."
|
||||
)
|
||||
|
||||
set(FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB "1" CACHE STRING
|
||||
"Size of the high-level draw commands buffer. Rule of thumb, 1 MB less than FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB, default 1."
|
||||
set(FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB "2" CACHE STRING
|
||||
"Size of the high-level draw commands buffer. Rule of thumb, 1 MB less than FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB, default 2."
|
||||
)
|
||||
|
||||
set(FILAMENT_MIN_COMMAND_BUFFERS_SIZE_IN_MB "1" CACHE STRING
|
||||
"Size of the command-stream buffer. As a rule of thumb use the same value as FILAMENT_PER_FRRAME_COMMANDS_SIZE_IN_MB, default 1."
|
||||
set(FILAMENT_MIN_COMMAND_BUFFERS_SIZE_IN_MB "2" CACHE STRING
|
||||
"Size of the command-stream buffer. As a rule of thumb use the same value as FILAMENT_PER_FRRAME_COMMANDS_SIZE_IN_MB, default 2."
|
||||
)
|
||||
|
||||
set(FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB "4" CACHE STRING
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.26.0'
|
||||
implementation 'com.google.android.filament:filament-android:1.27.0'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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:
|
||||
|
||||
```
|
||||
pod 'Filament', '~> 1.26.0'
|
||||
pod 'Filament', '~> 1.27.0'
|
||||
```
|
||||
|
||||
### Snapshots
|
||||
|
||||
@@ -5,6 +5,15 @@ A new header is inserted each time a *tag* is created.
|
||||
|
||||
## main branch
|
||||
|
||||
## v1.27.0
|
||||
|
||||
- WebGL: reduce max instance count to work around Chrome issues [⚠️ **Recompile Materials**]
|
||||
- engine: rework material/shader sampler binding code [⚠️ **Recompile Materials**]
|
||||
- gltfio: move the API for `recomputeBoundingBoxes` [⚠️ **API Change**]
|
||||
- engine: add support for specialization constants [⚠️ **Recompile Materials**]
|
||||
- gltfio: fix spotlight regression
|
||||
- gltfio: clear the MaterialInstance cache when creating new instances
|
||||
|
||||
## v1.26.0
|
||||
|
||||
- engine: new feature level APIs, see `Engine::getSupportedFeatureLevel()`
|
||||
|
||||
+1
-3
@@ -73,8 +73,6 @@ class ModelViewer(
|
||||
get() = resourceLoader.asyncGetLoadProgress()
|
||||
|
||||
var normalizeSkinningWeights = true
|
||||
var recomputeBoundingBoxes = false
|
||||
var ignoreBindTransform = false
|
||||
|
||||
var cameraFocalLength = 28f
|
||||
set(value) {
|
||||
@@ -116,7 +114,7 @@ class ModelViewer(
|
||||
|
||||
materialProvider = UbershaderProvider(engine)
|
||||
assetLoader = AssetLoader(engine, materialProvider, EntityManager.get())
|
||||
resourceLoader = ResourceLoader(engine, normalizeSkinningWeights, recomputeBoundingBoxes, ignoreBindTransform)
|
||||
resourceLoader = ResourceLoader(engine, normalizeSkinningWeights)
|
||||
|
||||
// Always add a direct light source since it is required for shadowing.
|
||||
// We highly recommend adding an indirect light as well.
|
||||
|
||||
@@ -36,11 +36,9 @@ static void destroy(void*, size_t, void *userData) {
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_gltfio_ResourceLoader_nCreateResourceLoader(JNIEnv*, jclass,
|
||||
jlong nativeEngine, jboolean normalizeSkinningWeights, jboolean recomputeBoundingBoxes,
|
||||
jboolean ignoreBindTransform) {
|
||||
jlong nativeEngine, jboolean normalizeSkinningWeights) {
|
||||
Engine* engine = (Engine*) nativeEngine;
|
||||
return (jlong) new ResourceLoader({ engine, {}, (bool) normalizeSkinningWeights,
|
||||
(bool) recomputeBoundingBoxes, (bool) ignoreBindTransform});
|
||||
return (jlong) new ResourceLoader({ engine, {}, (bool) normalizeSkinningWeights});
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
|
||||
-1
@@ -160,7 +160,6 @@ public class AssetLoader {
|
||||
* create/destroy churn, as noted above.
|
||||
*
|
||||
* This cannot be called after FilamentAsset#releaseSourceData().
|
||||
* Animation is not supported in new instances.
|
||||
* See also AssetLoader#createInstancedAsset().
|
||||
*/
|
||||
@Nullable
|
||||
|
||||
+4
-8
@@ -47,7 +47,7 @@ public class ResourceLoader {
|
||||
*/
|
||||
public ResourceLoader(@NonNull Engine engine) {
|
||||
long nativeEngine = engine.getNativeObject();
|
||||
mNativeObject = nCreateResourceLoader(nativeEngine, false, false, false);
|
||||
mNativeObject = nCreateResourceLoader(nativeEngine, false);
|
||||
mNativeStbProvider = nCreateStbProvider(nativeEngine);
|
||||
mNativeKtx2Provider = nCreateKtx2Provider(nativeEngine);
|
||||
nAddTextureProvider(mNativeObject, "image/jpeg", mNativeStbProvider);
|
||||
@@ -60,16 +60,12 @@ public class ResourceLoader {
|
||||
*
|
||||
* @param engine the engine that gets passed to all builder methods
|
||||
* @param normalizeSkinningWeights scale non-conformant skinning weights so they sum to 1
|
||||
* @param recomputeBoundingBoxes use computed bounding boxes rather than the ones in the asset
|
||||
* @param ignoreBindTransform ignore skinned primitives bind transform when compute bounding boxes
|
||||
* @throws IllegalAccessException
|
||||
* @throws InvocationTargetException
|
||||
*/
|
||||
public ResourceLoader(@NonNull Engine engine, boolean normalizeSkinningWeights,
|
||||
boolean recomputeBoundingBoxes, boolean ignoreBindTransform) {
|
||||
public ResourceLoader(@NonNull Engine engine, boolean normalizeSkinningWeights) {
|
||||
long nativeEngine = engine.getNativeObject();
|
||||
mNativeObject = nCreateResourceLoader(nativeEngine, normalizeSkinningWeights,
|
||||
recomputeBoundingBoxes, ignoreBindTransform);
|
||||
mNativeObject = nCreateResourceLoader(nativeEngine, normalizeSkinningWeights);
|
||||
mNativeStbProvider = nCreateStbProvider(nativeEngine);
|
||||
mNativeKtx2Provider = nCreateKtx2Provider(nativeEngine);
|
||||
nAddTextureProvider(mNativeObject, "image/jpeg", mNativeStbProvider);
|
||||
@@ -181,7 +177,7 @@ public class ResourceLoader {
|
||||
}
|
||||
|
||||
private static native long nCreateResourceLoader(long nativeEngine,
|
||||
boolean normalizeSkinningWeights, boolean recomputeBoundingBoxes, boolean ignoreBindTransform);
|
||||
boolean normalizeSkinningWeights);
|
||||
private static native void nDestroyResourceLoader(long nativeLoader);
|
||||
private static native void nAddResourceData(long nativeLoader, String url, Buffer buffer,
|
||||
int remaining);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.26.0
|
||||
VERSION_NAME=1.27.0
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
@@ -57,7 +57,8 @@ 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_BINDING_COUNT = 12; // 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.
|
||||
|
||||
/**
|
||||
* Defines the backend's feature levels.
|
||||
|
||||
@@ -280,7 +280,7 @@ public:
|
||||
}
|
||||
|
||||
size_t bpr = bpp * stride;
|
||||
size_t bprAligned = (bpr + (alignment - 1)) & -alignment;
|
||||
size_t bprAligned = (bpr + (alignment - 1)) & (~alignment + 1);
|
||||
return bprAligned * height;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <array>
|
||||
#include <variant>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -34,12 +35,12 @@ class Program {
|
||||
public:
|
||||
|
||||
static constexpr size_t SHADER_TYPE_COUNT = 2;
|
||||
static constexpr size_t BINDING_COUNT = CONFIG_BINDING_COUNT;
|
||||
static constexpr size_t UNIFORM_BINDING_COUNT = CONFIG_UNIFORM_BINDING_COUNT;
|
||||
static constexpr size_t SAMPLER_BINDING_COUNT = CONFIG_SAMPLER_BINDING_COUNT;
|
||||
|
||||
struct Sampler {
|
||||
utils::CString name = {}; // name of the sampler in the shader
|
||||
uint16_t binding = 0; // binding point of the sampler in the shader
|
||||
bool strict = false; // if true, this sampler must always have a bound texture
|
||||
uint32_t binding = 0; // binding point of the sampler in the shader
|
||||
};
|
||||
|
||||
struct SamplerGroupData {
|
||||
@@ -47,8 +48,8 @@ public:
|
||||
ShaderStageFlags stageFlags = ShaderStageFlags::ALL_SHADER_STAGE_FLAGS;
|
||||
};
|
||||
|
||||
using UniformBlockInfo = std::array<const char*, BINDING_COUNT>;
|
||||
using SamplerGroupInfo = std::array<SamplerGroupData, BINDING_COUNT>;
|
||||
using UniformBlockInfo = std::array<utils::CString, UNIFORM_BINDING_COUNT>;
|
||||
using SamplerGroupInfo = std::array<SamplerGroupData, SAMPLER_BINDING_COUNT>;
|
||||
using ShaderBlob = utils::FixedCapacityVector<uint8_t>;
|
||||
using ShaderSource = std::array<ShaderBlob, SHADER_TYPE_COUNT>;
|
||||
|
||||
@@ -75,7 +76,7 @@ public:
|
||||
// not permitted in glsl. The backend needs a way to associate a uniform block
|
||||
// to a binding point.
|
||||
Program& uniformBlockBindings(
|
||||
utils::FixedCapacityVector<std::pair<const char*, uint8_t>> const& uniformBlockBindings) noexcept;
|
||||
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> const& uniformBlockBindings) noexcept;
|
||||
|
||||
// sets the 'bindingPoint' sampler group descriptor for this program.
|
||||
// 'samplers' can be destroyed after this call.
|
||||
@@ -84,10 +85,20 @@ public:
|
||||
Program& setSamplerGroup(size_t bindingPoint, ShaderStageFlags stageFlags,
|
||||
Sampler const* samplers, size_t count) noexcept;
|
||||
|
||||
struct SpecializationConstant {
|
||||
uint32_t id; // id set in glsl
|
||||
std::variant<int32_t, float, bool> value; // value and type
|
||||
};
|
||||
|
||||
Program& specializationConstants(
|
||||
utils::FixedCapacityVector<SpecializationConstant> specConstants) noexcept;
|
||||
|
||||
|
||||
ShaderSource const& getShadersSource() const noexcept { return mShadersSource; }
|
||||
ShaderSource& getShadersSource() noexcept { return mShadersSource; }
|
||||
|
||||
UniformBlockInfo const& getUniformBlockBindings() const noexcept { return mUniformBlocks; }
|
||||
UniformBlockInfo& getUniformBlockBindings() noexcept { return mUniformBlocks; }
|
||||
|
||||
SamplerGroupInfo const& getSamplerGroupInfo() const { return mSamplerGroups; }
|
||||
SamplerGroupInfo& getSamplerGroupInfo() { return mSamplerGroups; }
|
||||
@@ -95,6 +106,13 @@ public:
|
||||
utils::CString const& getName() const noexcept { return mName; }
|
||||
utils::CString& getName() noexcept { return mName; }
|
||||
|
||||
utils::FixedCapacityVector<SpecializationConstant> const& getSpecializationConstants() const noexcept {
|
||||
return mSpecializationConstants;
|
||||
}
|
||||
utils::FixedCapacityVector<SpecializationConstant>& getSpecializationConstants() noexcept {
|
||||
return mSpecializationConstants;
|
||||
}
|
||||
|
||||
private:
|
||||
friend utils::io::ostream& operator<<(utils::io::ostream& out, const Program& builder);
|
||||
|
||||
@@ -103,6 +121,7 @@ private:
|
||||
ShaderSource mShadersSource;
|
||||
utils::CString mName;
|
||||
utils::Invocable<utils::io::ostream&(utils::io::ostream& out)> mLogger;
|
||||
utils::FixedCapacityVector<SpecializationConstant> mSpecializationConstants;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -21,15 +21,16 @@
|
||||
|
||||
#include <backend/PixelBufferDescriptor.h>
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
namespace filament::backend {
|
||||
|
||||
/**
|
||||
* Returns true if the shader string requests the Google-style line directive extension.
|
||||
*/
|
||||
bool requestsGoogleLineDirectivesExtension(const char* shader, size_t length) noexcept;
|
||||
bool requestsGoogleLineDirectivesExtension(std::string_view source) noexcept;
|
||||
|
||||
/**
|
||||
* Edit a GLSL shader string in-place so any Google-style line directives are turned into regular
|
||||
@@ -65,7 +66,6 @@ size_t getBlockHeight(TextureFormat format) noexcept;
|
||||
*/
|
||||
bool reshape(const PixelBufferDescriptor& data, PixelBufferDescriptor& reshaped);
|
||||
|
||||
} // namespace backend
|
||||
} // namespace filament
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_PRIVATE_BACKENDUTILS_H
|
||||
|
||||
@@ -22,12 +22,10 @@
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
namespace filament::backend {
|
||||
|
||||
bool requestsGoogleLineDirectivesExtension(const char* shader, size_t length) noexcept {
|
||||
std::string_view s(shader, length);
|
||||
return s.find("GL_GOOGLE_cpp_style_line_directive") != std::string_view::npos;
|
||||
bool requestsGoogleLineDirectivesExtension(std::string_view source) noexcept {
|
||||
return source.find("GL_GOOGLE_cpp_style_line_directive") != std::string_view::npos;
|
||||
}
|
||||
|
||||
void removeGoogleLineDirectives(char* shader, size_t length) noexcept {
|
||||
@@ -380,8 +378,7 @@ bool reshape(const PixelBufferDescriptor& data, PixelBufferDescriptor& reshaped)
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace backend
|
||||
} // namespace filament
|
||||
} // namespace backend::filament
|
||||
|
||||
|
||||
namespace utils {
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
|
||||
#include "backend/Program.h"
|
||||
|
||||
using namespace utils;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
using namespace utils;
|
||||
|
||||
// We want these in the .cpp file, so they're not inlined (not worth it)
|
||||
Program::Program() noexcept { // NOLINT(modernize-use-equals-default)
|
||||
}
|
||||
@@ -37,8 +37,8 @@ Program& Program::operator=(Program&& rhs) noexcept {
|
||||
|
||||
Program::~Program() noexcept = default;
|
||||
|
||||
Program& Program::diagnostics(utils::CString const& name,
|
||||
utils::Invocable<io::ostream&(utils::io::ostream&)>&& logger) {
|
||||
Program& Program::diagnostics(CString const& name,
|
||||
Invocable<io::ostream&(io::ostream&)>&& logger) {
|
||||
mName = name;
|
||||
mLogger = std::move(logger);
|
||||
return *this;
|
||||
@@ -52,9 +52,9 @@ Program& Program::shader(ShaderType shader, void const* data, size_t size) {
|
||||
}
|
||||
|
||||
Program& Program::uniformBlockBindings(
|
||||
utils::FixedCapacityVector<std::pair<const char*, uint8_t>> const& uniformBlockBindings) noexcept {
|
||||
FixedCapacityVector<std::pair<utils::CString, uint8_t>> const& uniformBlockBindings) noexcept {
|
||||
for (auto const& item : uniformBlockBindings) {
|
||||
assert_invariant(item.second < BINDING_COUNT);
|
||||
assert_invariant(item.second < UNIFORM_BINDING_COUNT);
|
||||
mUniformBlocks[item.second] = item.first;
|
||||
}
|
||||
return *this;
|
||||
@@ -71,6 +71,12 @@ Program& Program::setSamplerGroup(size_t bindingPoint, ShaderStageFlags stageFla
|
||||
return *this;
|
||||
}
|
||||
|
||||
Program& Program::specializationConstants(
|
||||
FixedCapacityVector<SpecializationConstant> specConstants) noexcept {
|
||||
mSpecializationConstants = std::move(specConstants);
|
||||
return *this;
|
||||
}
|
||||
|
||||
io::ostream& operator<<(io::ostream& out, const Program& builder) {
|
||||
out << "Program{";
|
||||
builder.mLogger(out);
|
||||
@@ -78,4 +84,5 @@ io::ostream& operator<<(io::ostream& out, const Program& builder) {
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -79,7 +79,7 @@ struct MetalContext {
|
||||
// State trackers.
|
||||
PipelineStateTracker pipelineState;
|
||||
DepthStencilStateTracker depthStencilState;
|
||||
UniformBufferState uniformState[VERTEX_BUFFER_START];
|
||||
UniformBufferState uniformState[UNIFORM_BUFFER_COUNT];
|
||||
CullModeStateTracker cullModeState;
|
||||
WindingStateTracker windingState;
|
||||
|
||||
|
||||
@@ -915,6 +915,7 @@ void MetalDriver::commit(Handle<HwSwapChain> sch) {
|
||||
}
|
||||
|
||||
void MetalDriver::bindUniformBuffer(uint32_t index, Handle<HwBufferObject> boh) {
|
||||
assert_invariant(index < UNIFORM_BUFFER_COUNT);
|
||||
auto* bo = handle_cast<MetalBufferObject>(boh);
|
||||
auto* currentBo = mContext->uniformState[index].buffer;
|
||||
if (currentBo) {
|
||||
@@ -930,6 +931,7 @@ void MetalDriver::bindUniformBuffer(uint32_t index, Handle<HwBufferObject> boh)
|
||||
|
||||
void MetalDriver::bindUniformBufferRange(uint32_t index, Handle<HwBufferObject> boh,
|
||||
uint32_t offset, uint32_t size) {
|
||||
assert_invariant(index < UNIFORM_BUFFER_COUNT);
|
||||
auto* bo = handle_cast<MetalBufferObject>(boh);
|
||||
auto* currentBo = mContext->uniformState[index].buffer;
|
||||
if (currentBo) {
|
||||
@@ -1317,8 +1319,8 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
|
||||
[mContext->currentRenderPassEncoder setScissorRect:scissorRect];
|
||||
|
||||
// Bind uniform buffers.
|
||||
MetalBuffer* uniformsToBind[Program::BINDING_COUNT] = { nil };
|
||||
NSUInteger offsets[Program::BINDING_COUNT] = { 0 };
|
||||
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) {
|
||||
@@ -1326,8 +1328,8 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
|
||||
offsets[index] = state.offset;
|
||||
});
|
||||
MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder,
|
||||
0, MetalBuffer::Stage::VERTEX | MetalBuffer::Stage::FRAGMENT, uniformsToBind, offsets,
|
||||
Program::BINDING_COUNT);
|
||||
UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX | MetalBuffer::Stage::FRAGMENT,
|
||||
uniformsToBind, offsets, Program::UNIFORM_BINDING_COUNT);
|
||||
|
||||
// Enumerate all the sampler buffers for the program and check which textures and samplers need
|
||||
// to be bound.
|
||||
@@ -1428,10 +1430,10 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
|
||||
[mContext->currentRenderPassEncoder setFragmentSamplerStates:samplersToBindFragment
|
||||
withRange:fragmentSamplerRange];
|
||||
|
||||
// Bind the vertex buffers.
|
||||
// Bind the user vertex buffers.
|
||||
|
||||
MetalBuffer* buffers[MAX_VERTEX_BUFFER_COUNT];
|
||||
size_t vertexBufferOffsets[MAX_VERTEX_BUFFER_COUNT];
|
||||
MetalBuffer* buffers[MAX_VERTEX_BUFFER_COUNT] = {};
|
||||
size_t vertexBufferOffsets[MAX_VERTEX_BUFFER_COUNT] = {};
|
||||
size_t bufferIndex = 0;
|
||||
|
||||
auto vb = primitive->vertexBuffer;
|
||||
@@ -1449,14 +1451,14 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
|
||||
|
||||
const auto bufferCount = bufferIndex;
|
||||
MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder,
|
||||
VERTEX_BUFFER_START, MetalBuffer::Stage::VERTEX, buffers,
|
||||
USER_VERTEX_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX, buffers,
|
||||
vertexBufferOffsets, bufferCount);
|
||||
|
||||
// Bind the zero buffer, used for missing vertex attributes.
|
||||
static const char bytes[16] = { 0 };
|
||||
[mContext->currentRenderPassEncoder setVertexBytes:bytes
|
||||
length:16
|
||||
atIndex:(VERTEX_BUFFER_START + ZERO_VERTEX_BUFFER)];
|
||||
atIndex:ZERO_VERTEX_BUFFER_BINDING];
|
||||
|
||||
MetalIndexBuffer* indexBuffer = primitive->indexBuffer;
|
||||
|
||||
@@ -1517,7 +1519,7 @@ void MetalDriver::enumerateSamplerGroups(
|
||||
|
||||
void MetalDriver::enumerateBoundUniformBuffers(
|
||||
const std::function<void(const UniformBufferState&, MetalBuffer*, uint32_t)>& f) {
|
||||
for (uint32_t i = 0; i < Program::BINDING_COUNT; i++) {
|
||||
for (uint32_t i = 0; i < Program::UNIFORM_BINDING_COUNT; i++) {
|
||||
auto& thisUniform = mContext->uniformState[i];
|
||||
if (!thisUniform.bound) {
|
||||
continue;
|
||||
|
||||
@@ -125,7 +125,7 @@ public:
|
||||
MetalBuffer* getBuffer() { return &buffer; }
|
||||
|
||||
// Tracks which uniform buffers this buffer object is bound into.
|
||||
static_assert(Program::BINDING_COUNT <= 32);
|
||||
static_assert(Program::UNIFORM_BINDING_COUNT <= 32);
|
||||
utils::bitset32 boundUniformBuffers;
|
||||
|
||||
private:
|
||||
|
||||
@@ -296,9 +296,8 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalInde
|
||||
|
||||
vertexDescription = {};
|
||||
|
||||
// Each attribute gets its own vertex buffer.
|
||||
|
||||
uint32_t bufferIndex = 0;
|
||||
// Each attribute gets its own vertex buffer, starting at logical buffer 1.
|
||||
uint32_t bufferIndex = 1;
|
||||
for (uint32_t attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++) {
|
||||
const auto& attribute = vertexBuffer->attributes[attributeIndex];
|
||||
if (attribute.buffer == Attribute::BUFFER_UNUSED) {
|
||||
@@ -310,10 +309,10 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalInde
|
||||
// shader to read from missing vertex attributes.
|
||||
vertexDescription.attributes[attributeIndex] = {
|
||||
.format = format,
|
||||
.buffer = ZERO_VERTEX_BUFFER,
|
||||
.buffer = ZERO_VERTEX_BUFFER_LOGICAL_INDEX,
|
||||
.offset = 0
|
||||
};
|
||||
vertexDescription.layouts[ZERO_VERTEX_BUFFER] = {
|
||||
vertexDescription.layouts[ZERO_VERTEX_BUFFER_LOGICAL_INDEX] = {
|
||||
.step = MTLVertexStepFunctionConstant,
|
||||
.stride = 16
|
||||
};
|
||||
@@ -373,7 +372,22 @@ MetalProgram::MetalProgram(id<MTLDevice> device, const Program& program) noexcep
|
||||
return;
|
||||
}
|
||||
|
||||
*shaderFunctions[i] = [library newFunctionWithName:@"main0"];
|
||||
MTLFunctionConstantValues* constants = [MTLFunctionConstantValues new];
|
||||
auto const& specializationConstants = program.getSpecializationConstants();
|
||||
for (auto const& sc : specializationConstants) {
|
||||
const std::array<MTLDataType, 3> types{
|
||||
MTLDataTypeInt, MTLDataTypeFloat, MTLDataTypeBool };
|
||||
std::visit([&sc, constants, type = types[sc.value.index()]](auto&& arg) {
|
||||
[constants setConstantValue:&arg
|
||||
type:type
|
||||
atIndex:sc.id];
|
||||
}, sc.value);
|
||||
}
|
||||
|
||||
*shaderFunctions[i] = [library newFunctionWithName:@"main0"
|
||||
constantValues:constants
|
||||
error:&error];
|
||||
|
||||
}
|
||||
|
||||
// All stages of the program have compiled successfully, this is a valid program.
|
||||
|
||||
@@ -35,17 +35,33 @@ inline bool operator==(const SamplerParams& lhs, const SamplerParams& rhs) {
|
||||
return lhs.u == rhs.u;
|
||||
}
|
||||
|
||||
static constexpr uint32_t SAMPLER_GROUP_COUNT = Program::BINDING_COUNT;
|
||||
static constexpr uint32_t SAMPLER_BINDING_COUNT = MAX_SAMPLER_COUNT;
|
||||
static constexpr uint32_t VERTEX_BUFFER_START = Program::BINDING_COUNT;
|
||||
// Bindings Buffer name Count
|
||||
// ------------------------------------------------------
|
||||
// 0 Zero buffer (placeholder vertex buffer) 1
|
||||
// 1-16 Filament vertex buffers 16 limited by MAX_VERTEX_BUFFER_COUNT
|
||||
// 17-26 Uniform buffers 10
|
||||
// 27-30 Sampler Uniforms (argument buffers) 4 TODO: use arg buffers for samplers
|
||||
//
|
||||
// Total 31
|
||||
|
||||
// The "zero" buffer is a small buffer for missing attributes that resides in the vertex slot
|
||||
// immediately following any user-provided vertex buffers.
|
||||
static constexpr uint32_t ZERO_VERTEX_BUFFER = MAX_VERTEX_BUFFER_COUNT;
|
||||
static constexpr uint32_t SAMPLER_GROUP_COUNT = Program::SAMPLER_BINDING_COUNT;
|
||||
static constexpr uint32_t SAMPLER_BINDING_COUNT = MAX_SAMPLER_COUNT;
|
||||
|
||||
// The total number of vertex buffer "slots" that the Metal backend can bind.
|
||||
// + 1 to account for the zero buffer.
|
||||
static constexpr uint32_t VERTEX_BUFFER_COUNT = MAX_VERTEX_BUFFER_COUNT + 1;
|
||||
// + 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.
|
||||
static constexpr uint32_t LOGICAL_VERTEX_BUFFER_COUNT = MAX_VERTEX_BUFFER_COUNT + 1;
|
||||
|
||||
// The "zero" buffer is a small buffer for missing attributes.
|
||||
static constexpr uint32_t ZERO_VERTEX_BUFFER_LOGICAL_INDEX = 0u;
|
||||
static constexpr uint32_t ZERO_VERTEX_BUFFER_BINDING = 0u;
|
||||
|
||||
static constexpr uint32_t USER_VERTEX_BUFFER_BINDING_START = 1u;
|
||||
|
||||
// The max number of uniform buffers that the Metal backend can simultaneously bind.
|
||||
static constexpr uint32_t UNIFORM_BUFFER_COUNT = 10u;
|
||||
// This constant must match the equivalent in CodeGenerator.cpp.
|
||||
static constexpr uint32_t UNIFORM_BUFFER_BINDING_START = 17u;
|
||||
|
||||
// Forward declarations necessary here, definitions at end of file.
|
||||
inline bool operator==(const MTLViewport& lhs, const MTLViewport& rhs);
|
||||
@@ -58,7 +74,7 @@ inline bool operator!=(const MTLViewport& lhs, const MTLViewport& rhs);
|
||||
struct VertexDescription {
|
||||
struct Attribute {
|
||||
MTLVertexFormat format; // 8 bytes
|
||||
uint32_t buffer; // 4 bytes
|
||||
uint32_t buffer; // 4 bytes a logical vertex buffer index
|
||||
uint32_t offset; // 4 bytes
|
||||
};
|
||||
struct Layout {
|
||||
@@ -66,7 +82,8 @@ struct VertexDescription {
|
||||
uint64_t stride; // 8 bytes
|
||||
};
|
||||
Attribute attributes[MAX_VERTEX_ATTRIBUTE_COUNT] = {}; // 256 bytes
|
||||
Layout layouts[VERTEX_BUFFER_COUNT] = {}; // 272 bytes
|
||||
// layouts[n] represents the layout of the vertex buffer at logical index n
|
||||
Layout layouts[LOGICAL_VERTEX_BUFFER_COUNT] = {}; // 272 bytes
|
||||
|
||||
bool operator==(const VertexDescription& rhs) const noexcept {
|
||||
bool result = true;
|
||||
|
||||
@@ -38,18 +38,18 @@ id<MTLRenderPipelineState> PipelineStateCreator::operator()(id<MTLDevice> device
|
||||
if (vertexDescription.attributes[i].format > MTLVertexFormatInvalid) {
|
||||
const auto& attribute = vertexDescription.attributes[i];
|
||||
vertex.attributes[i].format = attribute.format;
|
||||
vertex.attributes[i].bufferIndex = VERTEX_BUFFER_START + attribute.buffer;
|
||||
vertex.attributes[i].bufferIndex = attribute.buffer;
|
||||
vertex.attributes[i].offset = attribute.offset;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < VERTEX_BUFFER_COUNT; i++) {
|
||||
for (uint32_t i = 0; i < LOGICAL_VERTEX_BUFFER_COUNT; i++) {
|
||||
if (vertexDescription.layouts[i].stride > 0) {
|
||||
const auto& layout = vertexDescription.layouts[i];
|
||||
vertex.layouts[VERTEX_BUFFER_START + i].stride = layout.stride;
|
||||
vertex.layouts[VERTEX_BUFFER_START + i].stepFunction = layout.step;
|
||||
vertex.layouts[i].stride = layout.stride;
|
||||
vertex.layouts[i].stepFunction = layout.step;
|
||||
if (layout.step == MTLVertexStepFunctionConstant) {
|
||||
vertex.layouts[VERTEX_BUFFER_START + i].stepRate = 0;
|
||||
vertex.layouts[i].stepRate = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2589,7 +2589,7 @@ void OpenGLDriver::bindUniformBufferRange(uint32_t index, Handle<HwBufferObject>
|
||||
|
||||
void OpenGLDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
|
||||
DEBUG_MARKER()
|
||||
assert_invariant(index < Program::BINDING_COUNT);
|
||||
assert_invariant(index < Program::SAMPLER_BINDING_COUNT);
|
||||
GLSamplerGroup* sb = handle_cast<GLSamplerGroup *>(sbh);
|
||||
mSamplerBindings[index] = sb;
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
|
||||
@@ -323,7 +323,7 @@ private:
|
||||
return pos->second;
|
||||
}
|
||||
|
||||
const std::array<GLSamplerGroup*, Program::BINDING_COUNT>& getSamplerBindings() const {
|
||||
const std::array<GLSamplerGroup*, Program::SAMPLER_BINDING_COUNT>& getSamplerBindings() const {
|
||||
return mSamplerBindings;
|
||||
}
|
||||
|
||||
@@ -344,7 +344,7 @@ private:
|
||||
void setViewportScissor(Viewport const& viewportScissor) noexcept;
|
||||
|
||||
// sampler buffer binding points (nullptr if not used)
|
||||
std::array<GLSamplerGroup*, Program::BINDING_COUNT> mSamplerBindings = {}; // 12 pointers
|
||||
std::array<GLSamplerGroup*, Program::SAMPLER_BINDING_COUNT> mSamplerBindings = {}; // 4 pointers
|
||||
|
||||
mutable tsl::robin_map<uint32_t, GLuint> mSamplerMap;
|
||||
mutable std::vector<GLTexture*> mExternalStreams;
|
||||
|
||||
@@ -51,13 +51,16 @@ OpenGLProgram::OpenGLProgram(OpenGLDriver& gld, Program&& programBuilder) noexce
|
||||
|
||||
OpenGLContext& context = gld.getContext();
|
||||
|
||||
mLazyInitializationData->uniformBlockInfo = programBuilder.getUniformBlockBindings();
|
||||
mLazyInitializationData->uniformBlockInfo = std::move(programBuilder.getUniformBlockBindings());
|
||||
mLazyInitializationData->samplerGroupInfo = std::move(programBuilder.getSamplerGroupInfo());
|
||||
|
||||
// this cannot fail because we check compilation status after linking the program
|
||||
// shaders[] is filled with id of shader stages present.
|
||||
OpenGLProgram::compileShaders(context, programBuilder.getShadersSource(),
|
||||
gl.shaders, mLazyInitializationData->shaderSourceCode);
|
||||
OpenGLProgram::compileShaders(context,
|
||||
std::move(programBuilder.getShadersSource()),
|
||||
programBuilder.getSpecializationConstants(),
|
||||
gl.shaders,
|
||||
mLazyInitializationData->shaderSourceCode);
|
||||
|
||||
gld.runAtNextRenderPass(this, [this]() {
|
||||
// by this point we must not have a GL program
|
||||
@@ -98,9 +101,22 @@ OpenGLProgram::~OpenGLProgram() noexcept {
|
||||
*/
|
||||
void OpenGLProgram::compileShaders(OpenGLContext& context,
|
||||
Program::ShaderSource shadersSource,
|
||||
utils::FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
|
||||
GLuint shaderIds[Program::SHADER_TYPE_COUNT],
|
||||
std::array<CString, Program::SHADER_TYPE_COUNT>& outShaderSourceCode) noexcept {
|
||||
|
||||
std::string specificationConstantString;
|
||||
for (auto const& sc : specializationConstants) {
|
||||
specificationConstantString += "#define SPIRV_CROSS_CONSTANT_ID_" + std::to_string(sc.id) + ' ';
|
||||
specificationConstantString += std::visit([](auto&& arg) {
|
||||
return std::to_string(arg);
|
||||
}, sc.value);
|
||||
specificationConstantString += '\n';
|
||||
}
|
||||
if (!specificationConstantString.empty()) {
|
||||
specificationConstantString += '\n';
|
||||
}
|
||||
|
||||
// build all shaders
|
||||
UTILS_NOUNROLL
|
||||
for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
|
||||
@@ -116,31 +132,67 @@ void OpenGLProgram::compileShaders(OpenGLContext& context,
|
||||
}
|
||||
|
||||
if (UTILS_LIKELY(!shadersSource[i].empty())) {
|
||||
Program::ShaderBlob const& shader = shadersSource[i];
|
||||
std::string_view shaderView(reinterpret_cast<const char*>(shader.data()), shader.size());
|
||||
std::string temp;
|
||||
Program::ShaderBlob& shader = shadersSource[i];
|
||||
|
||||
if (!context.ext.GOOGLE_cpp_style_line_directive) {
|
||||
// If usages of the Google-style line directive are present, remove them, as some
|
||||
// drivers don't allow the quotation marks.
|
||||
if (UTILS_UNLIKELY(requestsGoogleLineDirectivesExtension(
|
||||
shaderView.data(), shaderView.size()))) {
|
||||
temp = shaderView; // copy string
|
||||
removeGoogleLineDirectives(temp.data(), temp.size()); // length is unaffected
|
||||
shaderView = temp;
|
||||
}
|
||||
}
|
||||
// remove GOOGLE_cpp_style_line_directive
|
||||
std::string_view source = process_GOOGLE_cpp_style_line_directive(context,
|
||||
reinterpret_cast<char*>(shader.data()), shader.size());
|
||||
|
||||
// Tragically, OpenGL 4.1 doesn't support unpackHalf2x16 and
|
||||
// MacOS doesn't support GL_ARB_shading_language_packing
|
||||
if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL) {
|
||||
if (context.state.major == 4 && context.state.minor == 1 &&
|
||||
!context.ext.ARB_shading_language_packing) {
|
||||
if (temp.empty()) {
|
||||
temp = shaderView; // copy string
|
||||
}
|
||||
// add support for ARB_shading_language_packing if needed
|
||||
auto const packingFunctions = process_ARB_shading_language_packing(context);
|
||||
|
||||
std::string unpackHalf2x16{ R"(
|
||||
// split shader source, so we can insert the specification constants and the packing functions
|
||||
auto const [prolog, body] = splitShaderSource(source);
|
||||
|
||||
const std::array<const char*, 4> sources = {
|
||||
prolog.data(),
|
||||
specificationConstantString.c_str(),
|
||||
packingFunctions.data(),
|
||||
body.data()
|
||||
};
|
||||
|
||||
const std::array<GLint, 4> lengths = {
|
||||
(GLint)prolog.length(),
|
||||
(GLint)specificationConstantString.length(),
|
||||
(GLint)packingFunctions.length(),
|
||||
(GLint)body.length() - 1 // null terminated
|
||||
};
|
||||
|
||||
GLuint shaderId = glCreateShader(glShaderType);
|
||||
glShaderSource(shaderId, sources.size(), sources.data(), lengths.data());
|
||||
glCompileShader(shaderId);
|
||||
|
||||
#ifndef NDEBUG
|
||||
// for debugging we return the original shader source (without the modifications we
|
||||
// made here), otherwise the line numbers wouldn't match.
|
||||
outShaderSourceCode[i] = { source.data(), source.length() };
|
||||
#endif
|
||||
|
||||
shaderIds[i] = shaderId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If usages of the Google-style line directive are present, remove them, as some
|
||||
// drivers don't allow the quotation marks. This happens in-place.
|
||||
std::string_view OpenGLProgram::process_GOOGLE_cpp_style_line_directive(OpenGLContext& context,
|
||||
char* source, size_t len) noexcept {
|
||||
if (!context.ext.GOOGLE_cpp_style_line_directive) {
|
||||
if (UTILS_UNLIKELY(requestsGoogleLineDirectivesExtension({ source, len }))) {
|
||||
removeGoogleLineDirectives(source, len); // length is unaffected
|
||||
}
|
||||
}
|
||||
return { source, len };
|
||||
}
|
||||
|
||||
// Tragically, OpenGL 4.1 doesn't support unpackHalf2x16 and
|
||||
// macOS doesn't support GL_ARB_shading_language_packing
|
||||
std::string_view OpenGLProgram::process_ARB_shading_language_packing(OpenGLContext& context) noexcept {
|
||||
using namespace std::literals;
|
||||
if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL) {
|
||||
if (context.state.major == 4 && context.state.minor == 1 &&
|
||||
!context.ext.ARB_shading_language_packing) {
|
||||
return R"(
|
||||
|
||||
// these don't handle denormals, NaNs or inf
|
||||
float u16tofp32(highp uint v) {
|
||||
@@ -175,33 +227,29 @@ highp uint packHalf2x16(vec2 v) {
|
||||
highp uint y = fp32tou16(v.y);
|
||||
return (y << 16) | x;
|
||||
}
|
||||
)"};
|
||||
// a good point for insertion is just before the first occurrence of an uniform block
|
||||
auto pos = temp.find("layout(std140)");
|
||||
if (pos != std::string_view::npos) {
|
||||
temp.insert(pos, unpackHalf2x16);
|
||||
}
|
||||
shaderView = temp;
|
||||
}
|
||||
}
|
||||
|
||||
GLuint shaderId = glCreateShader(glShaderType);
|
||||
{ // scope for source/length (we don't want them to leak out)
|
||||
const char* const source = shaderView.data();
|
||||
|
||||
// the shader string is null terminated and the length includes the null character
|
||||
const GLint length = (GLint)shaderView.length() - 1;
|
||||
assert_invariant( source[length] == '\0' );
|
||||
|
||||
glShaderSource(shaderId, 1, &source, &length);
|
||||
glCompileShader(shaderId);
|
||||
#ifndef NDEBUG
|
||||
outShaderSourceCode[i] = { source, static_cast<size_t>(length) };
|
||||
#endif
|
||||
}
|
||||
shaderIds[i] = shaderId;
|
||||
)"sv;
|
||||
}
|
||||
}
|
||||
return ""sv;
|
||||
}
|
||||
|
||||
// split shader source code in two, the first section goes from the start to the line after the
|
||||
// last #extension, and the 2nd part goes from there to the end.
|
||||
std::array<std::string_view, 2> OpenGLProgram::splitShaderSource(std::string_view source) noexcept {
|
||||
auto start = source.find("#version");
|
||||
assert_invariant(start != std::string_view::npos);
|
||||
|
||||
auto pos = source.rfind("#extension");
|
||||
if (pos == std::string_view::npos) {
|
||||
pos = start;
|
||||
}
|
||||
|
||||
auto eol = source.find('\n', pos) + 1;
|
||||
assert_invariant(eol != std::string_view::npos);
|
||||
|
||||
std::string_view version = source.substr(start, eol - start);
|
||||
std::string_view body = source.substr(version.length(), source.length() - version.length());
|
||||
return { version, body };
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -271,9 +319,7 @@ void OpenGLProgram::initialize(OpenGLContext& context) {
|
||||
gl.program, gl.shaders, initializationData->shaderSourceCode);
|
||||
|
||||
if (UTILS_LIKELY(mValid)) {
|
||||
initializeProgramState(context, gl.program,
|
||||
initializationData->uniformBlockInfo,
|
||||
initializationData->samplerGroupInfo);
|
||||
initializeProgramState(context, gl.program, *initializationData);
|
||||
}
|
||||
|
||||
// and destroy all temporary init data
|
||||
@@ -287,16 +333,15 @@ void OpenGLProgram::initialize(OpenGLContext& context) {
|
||||
* checkProgramStatus() has been successfully called.
|
||||
*/
|
||||
void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint program,
|
||||
Program::UniformBlockInfo const& uniformBlockInfo,
|
||||
Program::SamplerGroupInfo const& samplerGroupInfo) noexcept {
|
||||
LazyInitializationData const& lazyInitializationData) noexcept {
|
||||
|
||||
// Note: This is only needed, because the layout(binding=) syntax is not permitted in glsl
|
||||
// (ES3.0 and GL4.1). The backend needs a way to associate a uniform block to a binding point.
|
||||
UTILS_NOUNROLL
|
||||
for (GLuint binding = 0, n = uniformBlockInfo.size(); binding < n; binding++) {
|
||||
const char* name = uniformBlockInfo[binding];
|
||||
if (name) {
|
||||
GLint index = glGetUniformBlockIndex(program, name);
|
||||
for (GLuint binding = 0, n = lazyInitializationData.uniformBlockInfo.size(); binding < n; binding++) {
|
||||
auto const& name = lazyInitializationData.uniformBlockInfo[binding];
|
||||
if (!name.empty()) {
|
||||
GLint index = glGetUniformBlockIndex(program, name.c_str());
|
||||
if (index >= 0) {
|
||||
glUniformBlockBinding(program, GLuint(index), binding);
|
||||
}
|
||||
@@ -308,8 +353,8 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
|
||||
uint8_t tmu = 0;
|
||||
|
||||
UTILS_NOUNROLL
|
||||
for (size_t i = 0, c = samplerGroupInfo.size(); i < c; i++) {
|
||||
auto const& samplers = samplerGroupInfo[i].samplers;
|
||||
for (size_t i = 0, c = lazyInitializationData.samplerGroupInfo.size(); i < c; i++) {
|
||||
auto const& samplers = lazyInitializationData.samplerGroupInfo[i].samplers;
|
||||
if (samplers.empty()) {
|
||||
// this binding point doesn't have any samplers, skip it.
|
||||
continue;
|
||||
@@ -336,7 +381,7 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
|
||||
// whole group.
|
||||
if (atLeastOneSamplerUsed) {
|
||||
// Cache the sampler uniform locations for each interface block
|
||||
mUsedBindingPoints[usedBindingCount] = i;
|
||||
mUsedSamplerBindingPoints[usedBindingCount] = i;
|
||||
usedBindingCount++;
|
||||
} else {
|
||||
tmu -= samplers.size();
|
||||
@@ -345,12 +390,12 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
|
||||
mUsedBindingsCount = usedBindingCount;
|
||||
}
|
||||
|
||||
void OpenGLProgram::updateSamplers(OpenGLDriver* gld) noexcept {
|
||||
void OpenGLProgram::updateSamplers(OpenGLDriver* gld) const noexcept {
|
||||
using GLTexture = OpenGLDriver::GLTexture;
|
||||
|
||||
// cache a few member variable locally, outside the loop
|
||||
auto const& UTILS_RESTRICT samplerBindings = gld->getSamplerBindings();
|
||||
auto const& UTILS_RESTRICT usedBindingPoints = mUsedBindingPoints;
|
||||
auto const& UTILS_RESTRICT usedBindingPoints = mUsedSamplerBindingPoints;
|
||||
|
||||
for (uint8_t i = 0, tmu = 0, n = mUsedBindingsCount; i < n; i++) {
|
||||
auto const binding = usedBindingPoints[i];
|
||||
|
||||
@@ -72,11 +72,26 @@ public:
|
||||
} gl; // 12 bytes
|
||||
|
||||
private:
|
||||
// keep these away from of other class attributes
|
||||
struct LazyInitializationData {
|
||||
Program::UniformBlockInfo uniformBlockInfo;
|
||||
Program::SamplerGroupInfo samplerGroupInfo;
|
||||
std::array<utils::CString, Program::SHADER_TYPE_COUNT> shaderSourceCode;
|
||||
};
|
||||
|
||||
static void compileShaders(OpenGLContext& context,
|
||||
Program::ShaderSource shadersSource,
|
||||
utils::FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
|
||||
GLuint shaderIds[Program::SHADER_TYPE_COUNT],
|
||||
std::array<utils::CString, Program::SHADER_TYPE_COUNT>& outShaderSourceCode) noexcept;
|
||||
|
||||
static std::string_view process_GOOGLE_cpp_style_line_directive(OpenGLContext& context,
|
||||
char* source, size_t len) noexcept;
|
||||
|
||||
static std::string_view process_ARB_shading_language_packing(OpenGLContext& context) noexcept;
|
||||
|
||||
static std::array<std::string_view, 2> splitShaderSource(std::string_view source) noexcept;
|
||||
|
||||
static GLuint linkProgram(const GLuint shaderIds[Program::SHADER_TYPE_COUNT]) noexcept;
|
||||
|
||||
static bool checkProgramStatus(const char* name,
|
||||
@@ -86,17 +101,9 @@ private:
|
||||
void initialize(OpenGLContext& context);
|
||||
|
||||
void initializeProgramState(OpenGLContext& context, GLuint program,
|
||||
Program::UniformBlockInfo const& uniformBlockInfo,
|
||||
Program::SamplerGroupInfo const& samplerGroupInfo) noexcept;
|
||||
LazyInitializationData const& lazyInitializationData) noexcept;
|
||||
|
||||
void updateSamplers(OpenGLDriver* gld) noexcept;
|
||||
|
||||
// keep these away from of other class attributes
|
||||
struct LazyInitializationData {
|
||||
Program::UniformBlockInfo uniformBlockInfo;
|
||||
Program::SamplerGroupInfo samplerGroupInfo;
|
||||
std::array<utils::CString, Program::SHADER_TYPE_COUNT> shaderSourceCode;
|
||||
};
|
||||
void updateSamplers(OpenGLDriver* gld) const noexcept;
|
||||
|
||||
// number of bindings actually used by this program
|
||||
uint8_t mUsedBindingsCount = 0u;
|
||||
@@ -109,7 +116,7 @@ private:
|
||||
union {
|
||||
// when mInitialized == true:
|
||||
// information about each USED sampler buffer per binding (no gaps)
|
||||
std::array<uint8_t, Program::BINDING_COUNT> mUsedBindingPoints; // 12 bytes
|
||||
std::array<uint8_t, Program::SAMPLER_BINDING_COUNT> mUsedSamplerBindingPoints; // 4 bytes
|
||||
// when mInitialized == false:
|
||||
// lazy initialization data pointer
|
||||
LazyInitializationData* mLazyInitializationData;
|
||||
|
||||
@@ -1824,7 +1824,7 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
|
||||
|
||||
VkDescriptorImageInfo iInfo[VulkanPipelineCache::SAMPLER_BINDING_COUNT] = {};
|
||||
|
||||
for (uint8_t samplerGroupIdx = 0; samplerGroupIdx < Program::BINDING_COUNT; samplerGroupIdx++) {
|
||||
for (uint8_t samplerGroupIdx = 0; samplerGroupIdx < Program::SAMPLER_BINDING_COUNT; samplerGroupIdx++) {
|
||||
const auto& samplerGroup = program->samplerGroupInfo[samplerGroupIdx];
|
||||
const auto& samplers = samplerGroup.samplers;
|
||||
if (samplers.empty()) {
|
||||
|
||||
@@ -65,6 +65,47 @@ VulkanProgram::VulkanProgram(VulkanContext& context, const Program& builder) noe
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create shader module.");
|
||||
}
|
||||
|
||||
// populate the specialization constants requirements right now
|
||||
auto const& specializationConstants = builder.getSpecializationConstants();
|
||||
if (!specializationConstants.empty()) {
|
||||
// Allocate a single heap block to store all the specialization constants structures
|
||||
// our supported types are int32, float and bool, so we use 4 bytes per data. bool will
|
||||
// just use the first byte.
|
||||
char* pStorage = (char*)malloc(
|
||||
sizeof(VkSpecializationInfo) +
|
||||
specializationConstants.size() * sizeof(VkSpecializationMapEntry) +
|
||||
specializationConstants.size() * 4);
|
||||
|
||||
VkSpecializationInfo* const pInfo = (VkSpecializationInfo*)pStorage;
|
||||
VkSpecializationMapEntry* const pEntries =
|
||||
(VkSpecializationMapEntry*)(pStorage + sizeof(VkSpecializationInfo));
|
||||
void* pData = pStorage + sizeof(VkSpecializationInfo) +
|
||||
specializationConstants.size() * sizeof(VkSpecializationMapEntry);
|
||||
|
||||
*pInfo = {
|
||||
.mapEntryCount = specializationConstants.size(),
|
||||
.pMapEntries = pEntries,
|
||||
.dataSize = specializationConstants.size() * 4,
|
||||
.pData = pData,
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < specializationConstants.size(); i++) {
|
||||
const uint32_t offset = uint32_t(i) * 4;
|
||||
std::visit([&](auto&& arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
pEntries[i] = {
|
||||
.constantID = specializationConstants[i].id,
|
||||
.offset = offset,
|
||||
.size = sizeof(arg)
|
||||
};
|
||||
T* const addr = (T*)((char*)pData + offset);
|
||||
*addr = arg;
|
||||
}, specializationConstants[i].value);
|
||||
}
|
||||
|
||||
bundle.specializationInfos = pInfo;
|
||||
}
|
||||
|
||||
// Output a warning because it's okay to encounter empty blobs, but it's not okay to use
|
||||
// this program handle in a draw call.
|
||||
if (missing) {
|
||||
@@ -90,6 +131,7 @@ VulkanProgram::VulkanProgram(VulkanContext& context, VkShaderModule vs, VkShader
|
||||
VulkanProgram::~VulkanProgram() {
|
||||
vkDestroyShaderModule(context.device, bundle.vertex, VKALLOC);
|
||||
vkDestroyShaderModule(context.device, bundle.fragment, VKALLOC);
|
||||
free(bundle.specializationInfos);
|
||||
}
|
||||
|
||||
// Creates a special "default" render target (i.e. associated with the swap chain)
|
||||
|
||||
@@ -49,7 +49,7 @@ static VkShaderStageFlags getShaderStageFlags(utils::bitset64 key, uint16_t bind
|
||||
utils::bitset64
|
||||
static getPipelineLayoutKey(const Program::SamplerGroupInfo& samplerGroupInfo) noexcept {
|
||||
utils::bitset64 key = {};
|
||||
for (uint32_t binding = 0; binding < Program::BINDING_COUNT; ++binding) {
|
||||
for (uint32_t binding = 0; binding < Program::SAMPLER_BINDING_COUNT; ++binding) {
|
||||
const auto& stageFlags = samplerGroupInfo[binding].stageFlags;
|
||||
for (const auto& sampler : samplerGroupInfo[binding].samplers) {
|
||||
if (any(stageFlags & ShaderStageFlags::VERTEX)) {
|
||||
@@ -371,7 +371,9 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
||||
|
||||
// If we reach this point, we need to create and stash a brand new pipeline object.
|
||||
shaderStages[0].module = mPipelineRequirements.shaders[0];
|
||||
shaderStages[0].pSpecializationInfo = mSpecializationRequirements;
|
||||
shaderStages[1].module = mPipelineRequirements.shaders[1];
|
||||
shaderStages[1].pSpecializationInfo = mSpecializationRequirements;
|
||||
|
||||
// Expand our size-optimized structs into the proper Vk structs.
|
||||
uint32_t numVertexAttribs = 0;
|
||||
@@ -581,6 +583,7 @@ void VulkanPipelineCache::bindProgram(const VulkanProgram& program) noexcept {
|
||||
mPipelineRequirements.shaders[ssi] = shaders[ssi];
|
||||
}
|
||||
mLayoutRequirements = getPipelineLayoutKey(program.samplerGroupInfo);
|
||||
mSpecializationRequirements = program.bundle.specializationInfos;
|
||||
}
|
||||
|
||||
void VulkanPipelineCache::bindRasterState(const RasterState& rasterState) noexcept {
|
||||
|
||||
@@ -56,7 +56,7 @@ public:
|
||||
VulkanPipelineCache(VulkanPipelineCache const&) = delete;
|
||||
VulkanPipelineCache& operator=(VulkanPipelineCache const&) = delete;
|
||||
|
||||
static constexpr uint32_t UBUFFER_BINDING_COUNT = Program::BINDING_COUNT;
|
||||
static constexpr uint32_t UBUFFER_BINDING_COUNT = Program::UNIFORM_BINDING_COUNT;
|
||||
static constexpr uint32_t SAMPLER_BINDING_COUNT = MAX_SAMPLER_COUNT;
|
||||
static constexpr uint32_t TARGET_BINDING_COUNT = MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT;
|
||||
static constexpr uint32_t SHADER_MODULE_COUNT = 2;
|
||||
@@ -66,9 +66,6 @@ public:
|
||||
static constexpr uint32_t DESCRIPTOR_TYPE_COUNT = 3;
|
||||
static constexpr uint32_t INITIAL_DESCRIPTOR_SET_POOL_SIZE = 512;
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic warning "-Wpadded"
|
||||
|
||||
// The VertexArray POD is an array of buffer targets and an array of attributes that refer to
|
||||
// those targets. It does not include any references to actual buffers, so you can think of it
|
||||
// as a vertex assembler configuration. For simplicity it contains fixed-size arrays and does
|
||||
@@ -82,8 +79,12 @@ public:
|
||||
struct ProgramBundle {
|
||||
VkShaderModule vertex;
|
||||
VkShaderModule fragment;
|
||||
VkSpecializationInfo* specializationInfos = nullptr;
|
||||
};
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic warning "-Wpadded"
|
||||
|
||||
// The RasterState POD contains standard graphics-related state like blending, culling, etc.
|
||||
// The following states are omitted because Filament never changes them:
|
||||
// >>> depthClampEnable, rasterizerDiscardEnable, depthBoundsTestEnable, stencilTestEnable
|
||||
@@ -289,17 +290,17 @@ private:
|
||||
|
||||
// Represents all the Vulkan state that comprises a bound descriptor set.
|
||||
struct DescriptorKey {
|
||||
VkBuffer uniformBuffers[UBUFFER_BINDING_COUNT]; // 96 0
|
||||
DescriptorImageInfo samplers[SAMPLER_BINDING_COUNT]; // 768 96
|
||||
DescriptorImageInfo inputAttachments[TARGET_BINDING_COUNT]; // 192 864
|
||||
uint32_t uniformBufferOffsets[UBUFFER_BINDING_COUNT]; // 48 1056
|
||||
uint32_t uniformBufferSizes[UBUFFER_BINDING_COUNT]; // 48 1104
|
||||
VkBuffer uniformBuffers[UBUFFER_BINDING_COUNT]; // 80 0
|
||||
DescriptorImageInfo samplers[SAMPLER_BINDING_COUNT]; // 768 80
|
||||
DescriptorImageInfo inputAttachments[TARGET_BINDING_COUNT]; // 192 848
|
||||
uint32_t uniformBufferOffsets[UBUFFER_BINDING_COUNT]; // 40 1040
|
||||
uint32_t uniformBufferSizes[UBUFFER_BINDING_COUNT]; // 40 1080
|
||||
};
|
||||
static_assert(offsetof(DescriptorKey, samplers) == 96);
|
||||
static_assert(offsetof(DescriptorKey, inputAttachments) == 864);
|
||||
static_assert(offsetof(DescriptorKey, uniformBufferOffsets) == 1056);
|
||||
static_assert(offsetof(DescriptorKey, uniformBufferSizes) == 1104);
|
||||
static_assert(sizeof(DescriptorKey) == 1152, "DescriptorKey must not have implicit padding.");
|
||||
static_assert(offsetof(DescriptorKey, samplers) == 80);
|
||||
static_assert(offsetof(DescriptorKey, inputAttachments) == 848);
|
||||
static_assert(offsetof(DescriptorKey, uniformBufferOffsets) == 1040);
|
||||
static_assert(offsetof(DescriptorKey, uniformBufferSizes) == 1080);
|
||||
static_assert(sizeof(DescriptorKey) == 1120, "DescriptorKey must not have implicit padding.");
|
||||
|
||||
using DescHashFn = utils::hash::MurmurHashFn<DescriptorKey>;
|
||||
|
||||
@@ -385,6 +386,7 @@ private:
|
||||
PipelineLayoutKey mLayoutRequirements = {};
|
||||
PipelineKey mPipelineRequirements = {};
|
||||
DescriptorKey mDescriptorRequirements = {};
|
||||
VkSpecializationInfo* mSpecializationRequirements = {};
|
||||
|
||||
// Current bindings for the pipeline layout, pipeline, and descriptor sets.
|
||||
PipelineLayoutKey mBoundLayout = {};
|
||||
|
||||
@@ -417,7 +417,7 @@ TEST_F(BackendTest, DepthMinify) {
|
||||
{
|
||||
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
Program::Sampler psamplers[] = { utils::CString("tex"), 0, false };
|
||||
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
|
||||
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
|
||||
prog.uniformBlockBindings({{"params", 1}});
|
||||
program = api.createProgram(std::move(prog));
|
||||
@@ -559,7 +559,7 @@ TEST_F(BackendTest, ColorResolve) {
|
||||
{
|
||||
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
Program::Sampler psamplers[] = { utils::CString("tex"), 0, false };
|
||||
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
|
||||
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
|
||||
prog.uniformBlockBindings({{"params", 1}});
|
||||
program = api.createProgram(std::move(prog));
|
||||
@@ -667,7 +667,7 @@ TEST_F(BackendTest, DepthResolve) {
|
||||
{
|
||||
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
Program::Sampler psamplers[] = { utils::CString("tex"), 0, false };
|
||||
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
|
||||
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
|
||||
prog.uniformBlockBindings({{"params", 1}});
|
||||
program = api.createProgram(std::move(prog));
|
||||
|
||||
@@ -135,7 +135,7 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
{
|
||||
ShaderGenerator shaderGen(fullscreenVs, fullscreenFs, sBackend, sIsMobilePlatform);
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
Program::Sampler psamplers[] = { utils::CString("tex"), 0, false };
|
||||
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
|
||||
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
|
||||
prog.uniformBlockBindings({{"params", 1}});
|
||||
program = api.createProgram(std::move(prog));
|
||||
|
||||
@@ -350,7 +350,7 @@ TEST_F(BackendTest, UpdateImage2D) {
|
||||
getSamplerTypeName(t.textureFormat), fragmentTemplate);
|
||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
Program::Sampler psamplers[] = { utils::CString("tex"), 0, false };
|
||||
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
|
||||
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
|
||||
program = api.createProgram(std::move(prog));
|
||||
|
||||
@@ -429,7 +429,7 @@ TEST_F(BackendTest, UpdateImageSRGB) {
|
||||
getSamplerTypeName(textureFormat), fragmentTemplate);
|
||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
Program::Sampler psamplers[] = { utils::CString("tex"), 0, false };
|
||||
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
|
||||
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
|
||||
ProgramHandle program = api.createProgram(std::move(prog));
|
||||
|
||||
@@ -515,7 +515,7 @@ TEST_F(BackendTest, UpdateImageMipLevel) {
|
||||
getSamplerTypeName(textureFormat), fragmentUpdateImageMip);
|
||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
Program::Sampler psamplers[] = { utils::CString("tex"), 0, false };
|
||||
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
|
||||
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
|
||||
ProgramHandle program = api.createProgram(std::move(prog));
|
||||
|
||||
@@ -587,7 +587,7 @@ TEST_F(BackendTest, UpdateImage3D) {
|
||||
getSamplerTypeName(samplerType), fragmentUpdateImage3DTemplate);
|
||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
Program::Sampler psamplers[] = { utils::CString("tex"), 0, false };
|
||||
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
|
||||
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
|
||||
ProgramHandle program = api.createProgram(std::move(prog));
|
||||
|
||||
|
||||
@@ -173,6 +173,29 @@ public:
|
||||
* Config is used to define the memory footprint used by the engine, such as the
|
||||
* command buffer size. Config can be used to customize engine requirements based
|
||||
* on the applications needs.
|
||||
*
|
||||
* .perRenderPassArenaSizeMB (default: 3 MiB)
|
||||
* +--------------------------+
|
||||
* | |
|
||||
* | .perFrameCommandsSizeMB |
|
||||
* | (default 2 MiB) |
|
||||
* | |
|
||||
* +--------------------------+
|
||||
* | (froxel, etc...) |
|
||||
* +--------------------------+
|
||||
*
|
||||
*
|
||||
* .commandBufferSizeMB (default 3MiB)
|
||||
* +--------------------------+
|
||||
* | .minCommandBufferSizeMB |
|
||||
* +--------------------------+
|
||||
* | .minCommandBufferSizeMB |
|
||||
* +--------------------------+
|
||||
* | .minCommandBufferSizeMB |
|
||||
* +--------------------------+
|
||||
* : :
|
||||
* : :
|
||||
*
|
||||
*/
|
||||
struct Config {
|
||||
/**
|
||||
@@ -190,7 +213,7 @@ public:
|
||||
|
||||
|
||||
/**
|
||||
* Size in MiB of the per-frame high level command buffer arena.
|
||||
* Size in MiB of the per-frame data arena.
|
||||
*
|
||||
* This is the main arena used for allocations when preparing a frame.
|
||||
* e.g.: Froxel data and high-level commands are allocated from this arena.
|
||||
|
||||
@@ -327,7 +327,8 @@ public:
|
||||
* PixelBufferDescriptor&& buffer)
|
||||
*/
|
||||
inline void setImage(Engine& engine, size_t level, PixelBufferDescriptor&& buffer) const {
|
||||
setImage(engine, level, 0, 0, 0, getWidth(level), getHeight(level), 1, std::move(buffer));
|
||||
setImage(engine, level, 0, 0, 0,
|
||||
uint32_t(getWidth(level)), uint32_t(getHeight(level)), 1, std::move(buffer));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+41
-26
@@ -49,28 +49,34 @@ static constexpr size_t FROXEL_SLICE_COUNT = 16;
|
||||
static constexpr float FROXEL_FIRST_SLICE_DEPTH = 5;
|
||||
static constexpr float FROXEL_LAST_SLICE_DISTANCE = 100;
|
||||
|
||||
// Max number of froxels limited by:
|
||||
// - max texture size [min 2048]
|
||||
// - chosen texture width [64]
|
||||
//
|
||||
// Also, increasing the number of froxels adds more pressure on the "record buffer" which stores
|
||||
// the light indices per froxel. The record buffer is limited to min(16K[ubo], 64K[uint16]) entries,
|
||||
// so with 8192 froxels, we can store 2 lights per froxels assuming they're all used. In practice, some
|
||||
// froxels are not used, so we can store more.
|
||||
constexpr size_t FROXEL_BUFFER_ENTRY_COUNT = 8192;
|
||||
|
||||
// The record buffer is limited by both the UBO size and our use of 16-bits indices.
|
||||
constexpr size_t RECORD_BUFFER_ENTRY_COUNT = CONFIG_MINSPEC_UBO_SIZE; // 16 KiB UBO minspec
|
||||
|
||||
// The Froxel buffer is set to FROXEL_BUFFER_WIDTH x n
|
||||
// With n limited by the supported texture dimension, which is guaranteed to be at least 2048
|
||||
// in all version of GLES.
|
||||
|
||||
// Make sure this matches the same constants in shading_lit.fs
|
||||
constexpr size_t FROXEL_BUFFER_WIDTH_SHIFT = 6u;
|
||||
constexpr size_t FROXEL_BUFFER_WIDTH = 1u << FROXEL_BUFFER_WIDTH_SHIFT;
|
||||
constexpr size_t FROXEL_BUFFER_WIDTH_MASK = FROXEL_BUFFER_WIDTH - 1u;
|
||||
constexpr size_t FROXEL_BUFFER_HEIGHT = (FROXEL_BUFFER_ENTRY_COUNT_MAX + FROXEL_BUFFER_WIDTH_MASK) / FROXEL_BUFFER_WIDTH;
|
||||
|
||||
constexpr size_t RECORD_BUFFER_WIDTH_SHIFT = 4u;
|
||||
constexpr size_t RECORD_BUFFER_WIDTH = 1u << RECORD_BUFFER_WIDTH_SHIFT;
|
||||
constexpr size_t RECORD_BUFFER_HEIGHT = 1024;
|
||||
constexpr size_t RECORD_BUFFER_ENTRY_COUNT = RECORD_BUFFER_WIDTH * RECORD_BUFFER_HEIGHT; // 16K
|
||||
constexpr size_t FROXEL_BUFFER_HEIGHT = (FROXEL_BUFFER_ENTRY_COUNT + FROXEL_BUFFER_WIDTH_MASK) / FROXEL_BUFFER_WIDTH;
|
||||
|
||||
// Buffer needed for Froxelizer internal data structures (~256 KiB)
|
||||
constexpr size_t PER_FROXELDATA_ARENA_SIZE = sizeof(float4) *
|
||||
(FROXEL_BUFFER_ENTRY_COUNT_MAX +
|
||||
FROXEL_BUFFER_ENTRY_COUNT_MAX + 3 +
|
||||
(FROXEL_BUFFER_ENTRY_COUNT +
|
||||
FROXEL_BUFFER_ENTRY_COUNT + 3 +
|
||||
FROXEL_SLICE_COUNT / 4 + 1);
|
||||
|
||||
|
||||
// number of lights processed by one group (e.g. 32)
|
||||
static constexpr size_t LIGHT_PER_GROUP = sizeof(Froxelizer::LightGroupType) * 8;
|
||||
|
||||
@@ -78,12 +84,22 @@ static constexpr size_t LIGHT_PER_GROUP = sizeof(Froxelizer::LightGroupType) * 8
|
||||
static constexpr size_t GROUP_COUNT =
|
||||
(CONFIG_MAX_LIGHT_COUNT + LIGHT_PER_GROUP - 1) / LIGHT_PER_GROUP;
|
||||
|
||||
// This depends on the maximum number of lights (currently 256)
|
||||
static_assert(CONFIG_MAX_LIGHT_INDEX <= std::numeric_limits<Froxelizer::RecordBufferType>::max(),
|
||||
"can't have more than 256 lights");
|
||||
|
||||
// record buffer cannot be larger than 65K entries because we're using uint16_t to store indices
|
||||
// so its maximum size is 128 KiB
|
||||
// Record buffer cannot be larger than 65K entries because froxels use uint16_t to store indices
|
||||
// to it.
|
||||
static_assert(RECORD_BUFFER_ENTRY_COUNT <= 65536,
|
||||
"RecordBuffer cannot be larger than 65536 entries");
|
||||
|
||||
static_assert(RECORD_BUFFER_ENTRY_COUNT <= CONFIG_MINSPEC_UBO_SIZE,
|
||||
"RecordBuffer cannot be larger than the UBO minspec (16KiB)");
|
||||
|
||||
struct Froxelizer::FroxelThreadData :
|
||||
public std::array<LightGroupType, FROXEL_BUFFER_ENTRY_COUNT> {
|
||||
};
|
||||
|
||||
Froxelizer::Froxelizer(FEngine& engine)
|
||||
: mArena("froxel", PER_FROXELDATA_ARENA_SIZE),
|
||||
mZLightNear(FROXEL_FIRST_SLICE_DEPTH),
|
||||
@@ -156,14 +172,14 @@ bool Froxelizer::prepare(
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocations that need to persists until the driver consumes them are done from
|
||||
* Allocations that need to persist until the driver consumes them are done from
|
||||
* the command stream.
|
||||
*/
|
||||
|
||||
// froxel buffer (~32 KiB)
|
||||
mFroxelBufferUser = {
|
||||
driverApi.allocatePod<FroxelEntry>(FROXEL_BUFFER_ENTRY_COUNT_MAX),
|
||||
FROXEL_BUFFER_ENTRY_COUNT_MAX };
|
||||
driverApi.allocatePod<FroxelEntry>(FROXEL_BUFFER_ENTRY_COUNT),
|
||||
FROXEL_BUFFER_ENTRY_COUNT };
|
||||
|
||||
// record buffer (~16 KiB)
|
||||
mRecordBufferUser = {
|
||||
@@ -176,8 +192,8 @@ bool Froxelizer::prepare(
|
||||
|
||||
// light records per froxel (~256 KiB)
|
||||
mLightRecords = {
|
||||
arena.allocate<LightRecord>(FROXEL_BUFFER_ENTRY_COUNT_MAX, CACHELINE_SIZE),
|
||||
FROXEL_BUFFER_ENTRY_COUNT_MAX };
|
||||
arena.allocate<LightRecord>(FROXEL_BUFFER_ENTRY_COUNT, CACHELINE_SIZE),
|
||||
FROXEL_BUFFER_ENTRY_COUNT };
|
||||
|
||||
// froxel thread data (~256 KiB)
|
||||
mFroxelShardedData = {
|
||||
@@ -208,7 +224,7 @@ void Froxelizer::computeFroxelLayout(
|
||||
// calculate froxel dimension from FROXEL_BUFFER_ENTRY_COUNT_MAX and viewport
|
||||
// - Start from the maximum number of froxels we can use in the x-y plane
|
||||
size_t froxelSliceCount = FROXEL_SLICE_COUNT;
|
||||
size_t froxelPlaneCount = FROXEL_BUFFER_ENTRY_COUNT_MAX / froxelSliceCount;
|
||||
size_t froxelPlaneCount = FROXEL_BUFFER_ENTRY_COUNT / froxelSliceCount;
|
||||
// - compute the number of square froxels we need in width and height, rounded down
|
||||
// solving: | froxelCountX * froxelCountY == froxelPlaneCount
|
||||
// | froxelCountX / froxelCountY == width / height
|
||||
@@ -248,8 +264,8 @@ bool Froxelizer::update() noexcept {
|
||||
computeFroxelLayout(&froxelDimension, &froxelCountX, &froxelCountY, &froxelCountZ, viewport);
|
||||
|
||||
mFroxelDimension = froxelDimension;
|
||||
mClipToFroxelX = (0.5f * viewport.width) / froxelDimension.x;
|
||||
mClipToFroxelY = (0.5f * viewport.height) / froxelDimension.y;
|
||||
mClipToFroxelX = (0.5f * float(viewport.width)) / float(froxelDimension.x);
|
||||
mClipToFroxelY = (0.5f * float(viewport.height)) / float(froxelDimension.y);
|
||||
|
||||
uniformsNeedUpdating = true;
|
||||
|
||||
@@ -258,15 +274,14 @@ bool Froxelizer::update() noexcept {
|
||||
<< froxelDimension.x << "x" << froxelDimension.y << io::endl
|
||||
<< "Froxel: " << froxelCountX << "x" << froxelCountY << "x" << froxelCountZ
|
||||
<< " = " << (froxelCountX * froxelCountY * froxelCountZ)
|
||||
<< " (" << FROXEL_BUFFER_ENTRY_COUNT_MAX - froxelCountX * froxelCountY * froxelCountZ << " lost)"
|
||||
<< " (" << FROXEL_BUFFER_ENTRY_COUNT - froxelCountX * froxelCountY * froxelCountZ << " lost)"
|
||||
<< io::endl;
|
||||
#endif
|
||||
|
||||
mFroxelCountX = froxelCountX;
|
||||
mFroxelCountY = froxelCountY;
|
||||
mFroxelCountZ = froxelCountZ;
|
||||
// froxel count must fit on 16 bits
|
||||
const uint16_t froxelCount = uint16_t(froxelCountX * froxelCountY * froxelCountZ);
|
||||
const uint32_t froxelCount = uint32_t(froxelCountX * froxelCountY * froxelCountZ);
|
||||
mFroxelCount = froxelCount;
|
||||
|
||||
if (mDistancesZ) {
|
||||
@@ -629,7 +644,7 @@ void Froxelizer::froxelizeAssignRecordsCompress() noexcept {
|
||||
// this gets very well vectorized...
|
||||
|
||||
utils::Slice<LightRecord> records(mLightRecords);
|
||||
for (size_t j = 0, jc = FROXEL_BUFFER_ENTRY_COUNT_MAX; j < jc; j++) {
|
||||
for (size_t j = 0, jc = FROXEL_BUFFER_ENTRY_COUNT; j < jc; j++) {
|
||||
for (size_t i = 0; i < LightRecord::bitset::WORLD_COUNT; i++) {
|
||||
using container_type = LightRecord::bitset::container_type;
|
||||
constexpr size_t r = sizeof(container_type) / sizeof(LightGroupType);
|
||||
@@ -642,7 +657,7 @@ void Froxelizer::froxelizeAssignRecordsCompress() noexcept {
|
||||
}
|
||||
|
||||
LightRecord::bitset allLights{};
|
||||
for (size_t j = 0, jc = FROXEL_BUFFER_ENTRY_COUNT_MAX; j < jc; j++) {
|
||||
for (size_t j = 0, jc = FROXEL_BUFFER_ENTRY_COUNT; j < jc; j++) {
|
||||
allLights |= records[j].lights;
|
||||
}
|
||||
|
||||
@@ -670,7 +685,7 @@ void Froxelizer::froxelizeAssignRecordsCompress() noexcept {
|
||||
// how many froxel record entries were reused (for debugging)
|
||||
UTILS_UNUSED size_t reused = 0;
|
||||
|
||||
for (size_t i = 0, c = getFroxelCount(); i < c;) {
|
||||
for (size_t i = 0, c = mFroxelCount; i < c;) {
|
||||
LightRecord b = records[i];
|
||||
if (b.lights.none()) {
|
||||
froxels[i++].u32 = 0;
|
||||
|
||||
+22
-28
@@ -48,9 +48,10 @@ public:
|
||||
};
|
||||
|
||||
//
|
||||
// Light UBO Froxel Record Buffer per-froxel light list texture
|
||||
// {4 x float4} R_U8 {index into RG_U16 {offset, point-count, spot-count}
|
||||
// Light UBO Froxel Record UBO per-froxel light list texture
|
||||
// {4 x float4} {index into RG_U16 {offset, point-count, spot-count}
|
||||
// (spot/point light texture}
|
||||
// {uint4 -> 16 indices}
|
||||
//
|
||||
// +----+ +-+ +----+
|
||||
// 0|....| <------------+ 0| | +-----------|0230| (e.g. offset=02, 3-lights)
|
||||
@@ -69,16 +70,6 @@ public:
|
||||
// 256 lights max
|
||||
//
|
||||
|
||||
// Max number of froxels limited by:
|
||||
// - max texture size [min 2048]
|
||||
// - chosen texture width [64]
|
||||
// - size of CPU-side indices [16 bits]
|
||||
// Also, increasing the number of froxels adds more pressure on the "record buffer" which stores
|
||||
// the light indices per froxel. The record buffer is limited to 65536 entries, so with
|
||||
// 8192 froxels, we can store 8 lights per froxels assuming they're all used. In practice, some
|
||||
// froxels are not used, so we can store more.
|
||||
static constexpr size_t FROXEL_BUFFER_ENTRY_COUNT_MAX = 8192;
|
||||
|
||||
class Froxelizer {
|
||||
public:
|
||||
explicit Froxelizer(FEngine& engine);
|
||||
@@ -100,8 +91,8 @@ public:
|
||||
* Allocate per-frame data structures for froxelization.
|
||||
*
|
||||
* driverApi used to allocate memory in the stream
|
||||
* arena use to allocate per-frame memory
|
||||
* viewport viewport used to calculate froxel dimensions
|
||||
* arena used to allocate per-frame memory
|
||||
* viewport used to calculate froxel dimensions
|
||||
* projection camera projection matrix
|
||||
* projectionNear near plane
|
||||
* projectionFar far plane
|
||||
@@ -147,9 +138,10 @@ public:
|
||||
};
|
||||
};
|
||||
};
|
||||
// This depends on the maximum number of lights (currently 255),and can't be more than 16 bits.
|
||||
static_assert(CONFIG_MAX_LIGHT_INDEX <= std::numeric_limits<uint16_t>::max(), "can't have more than 65536 lights");
|
||||
using RecordBufferType = std::conditional_t<CONFIG_MAX_LIGHT_INDEX <= std::numeric_limits<uint8_t>::max(), uint8_t, uint16_t>;
|
||||
|
||||
// we can't change this easily because the shader expects 16 indices per uint4
|
||||
using RecordBufferType = uint8_t;
|
||||
|
||||
const utils::Slice<FroxelEntry>& getFroxelBufferUser() const { return mFroxelBufferUser; }
|
||||
const utils::Slice<RecordBufferType>& getRecordBufferUser() const { return mRecordBufferUser; }
|
||||
|
||||
@@ -185,7 +177,7 @@ private:
|
||||
uint16_t reserved;
|
||||
};
|
||||
|
||||
using FroxelThreadData = std::array<LightGroupType, FROXEL_BUFFER_ENTRY_COUNT_MAX>;
|
||||
struct FroxelThreadData;
|
||||
|
||||
inline void setViewport(Viewport const& viewport) noexcept;
|
||||
inline void setProjection(const math::mat4f& projection, float near, float far) noexcept;
|
||||
@@ -203,8 +195,8 @@ private:
|
||||
utils::Slice<RecordBufferType> const& lightList,
|
||||
const FScene::LightSoa& lightData, size_t lightRecordsOffset) noexcept;
|
||||
|
||||
uint16_t getFroxelIndex(size_t ix, size_t iy, size_t iz) const noexcept {
|
||||
return uint16_t(ix + (iy * mFroxelCountX) + (iz * mFroxelCountX * mFroxelCountY));
|
||||
size_t getFroxelIndex(size_t ix, size_t iy, size_t iz) const noexcept {
|
||||
return ix + (iy * mFroxelCountX) + (iz * mFroxelCountX * mFroxelCountY);
|
||||
}
|
||||
|
||||
size_t findSliceZ(float viewSpaceZ) const noexcept UTILS_PURE;
|
||||
@@ -215,25 +207,27 @@ private:
|
||||
math::uint2* dim, uint16_t* countX, uint16_t* countY, uint16_t* countZ,
|
||||
Viewport const& viewport) noexcept;
|
||||
|
||||
// internal state dependant on the viewport and needed for froxelizing
|
||||
LinearAllocatorArena mArena; // ~256 KiB
|
||||
// internal state dependent on the viewport and needed for froxelizing
|
||||
LinearAllocatorArena mArena; // ~256 KiB
|
||||
|
||||
float* mDistancesZ = nullptr; // max 2.1 MiB (actual: resolution dependant)
|
||||
// allocations in the private froxel arena
|
||||
float* mDistancesZ = nullptr;
|
||||
math::float4* mPlanesX = nullptr;
|
||||
math::float4* mPlanesY = nullptr;
|
||||
math::float4* mBoundingSpheres = nullptr;
|
||||
math::float4* mBoundingSpheres = nullptr; // 128 KiB w/ 8192 froxels
|
||||
|
||||
utils::Slice<FroxelThreadData> mFroxelShardedData; // 256 KiB w/ 256 lights
|
||||
// allocations in the per frame arena
|
||||
utils::Slice<FroxelThreadData> mFroxelShardedData; // 256 KiB w/ 256 lights and 8192 froxels
|
||||
utils::Slice<FroxelEntry> mFroxelBufferUser; // 32 KiB w/ 8192 froxels
|
||||
utils::Slice<LightRecord> mLightRecords; // 256 KiB w/ 256 lights
|
||||
|
||||
// max 32 KiB (actual: resolution dependant)
|
||||
// allocations in the command stream
|
||||
utils::Slice<RecordBufferType> mRecordBufferUser; // 16 KiB
|
||||
utils::Slice<LightRecord> mLightRecords; // 256 KiB w/ 256 lights
|
||||
|
||||
uint16_t mFroxelCountX = 0;
|
||||
uint16_t mFroxelCountY = 0;
|
||||
uint16_t mFroxelCountZ = 0;
|
||||
uint16_t mFroxelCount = 0;
|
||||
uint32_t mFroxelCount = 0;
|
||||
math::uint2 mFroxelDimension = {};
|
||||
|
||||
math::mat4f mProjection;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <filament/MaterialChunkType.h>
|
||||
|
||||
#include <private/filament/SamplerBindingsInfo.h>
|
||||
#include <private/filament/SamplerInterfaceBlock.h>
|
||||
#include <private/filament/UniformInterfaceBlock.h>
|
||||
#include <private/filament/SubpassInfo.h>
|
||||
@@ -171,6 +172,17 @@ bool MaterialParser::getUniformBlockBindings(
|
||||
return ChunkUniformBlockBindings::unflatten(unflattener, value);
|
||||
}
|
||||
|
||||
bool MaterialParser::getSamplerBlockBindings(
|
||||
SamplerGroupBindingInfoList* pSamplerGroupInfoList,
|
||||
SamplerBindingToNameMap* pSamplerBindingToNameMap) const noexcept {
|
||||
auto type = MaterialSamplerBindings;
|
||||
const uint8_t* start = mImpl.mChunkContainer.getChunkStart(type);
|
||||
const uint8_t* end = mImpl.mChunkContainer.getChunkEnd(type);
|
||||
Unflattener unflattener(start, end);
|
||||
return ChunkSamplerBlockBindings::unflatten(unflattener,
|
||||
pSamplerGroupInfoList, pSamplerBindingToNameMap);
|
||||
}
|
||||
|
||||
bool MaterialParser::getDepthWriteSet(bool* value) const noexcept {
|
||||
return mImpl.getFromSimpleChunk(ChunkType::MaterialDepthWriteSet, value);
|
||||
}
|
||||
@@ -486,4 +498,48 @@ bool ChunkUniformBlockBindings::unflatten(filaflat::Unflattener& unflattener,
|
||||
}
|
||||
|
||||
|
||||
bool ChunkSamplerBlockBindings::unflatten(Unflattener& unflattener,
|
||||
SamplerGroupBindingInfoList* pSamplerGroupBindingInfoList,
|
||||
SamplerBindingToNameMap* pSamplerBindingToNameMap) {
|
||||
assert_invariant(pSamplerGroupBindingInfoList && pSamplerBindingToNameMap);
|
||||
SamplerGroupBindingInfoList& samplerGroupBindingInfoList = *pSamplerGroupBindingInfoList;
|
||||
SamplerBindingToNameMap& samplerBindingToNameMap = *pSamplerBindingToNameMap;
|
||||
|
||||
uint8_t count;
|
||||
if (!unflattener.read(&count)) {
|
||||
return false;
|
||||
}
|
||||
assert_invariant(count == utils::Enum::count<SamplerBindingPoints>());
|
||||
|
||||
UTILS_NOUNROLL
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (!unflattener.read(&samplerGroupBindingInfoList[i].bindingOffset)) {
|
||||
return false;
|
||||
}
|
||||
if (!unflattener.read((uint8_t *)&samplerGroupBindingInfoList[i].shaderStageFlags)) {
|
||||
return false;
|
||||
}
|
||||
if (!unflattener.read(&samplerGroupBindingInfoList[i].count)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!unflattener.read(&count)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
uint8_t binding;
|
||||
if (!unflattener.read(&binding)) {
|
||||
return false;
|
||||
}
|
||||
assert_invariant(binding < backend::MAX_SAMPLER_COUNT);
|
||||
if (!unflattener.read(&samplerBindingToNameMap[binding])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -21,14 +21,17 @@
|
||||
#include <filaflat/MaterialChunk.h>
|
||||
|
||||
#include <filament/MaterialEnums.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <filament/MaterialChunkType.h>
|
||||
|
||||
#include "../../libs/filamat/src/SamplerBindingMap.h"
|
||||
#include <private/filament/Variant.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/CString.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "private/filament/Variant.h"
|
||||
|
||||
namespace filaflat {
|
||||
class ChunkContainer;
|
||||
@@ -66,6 +69,8 @@ public:
|
||||
bool getShaderModels(uint32_t* value) const noexcept;
|
||||
bool getMaterialProperties(uint64_t* value) const noexcept;
|
||||
bool getUniformBlockBindings(utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>>* value) const noexcept;
|
||||
bool getSamplerBlockBindings(SamplerGroupBindingInfoList* pSamplerGroupInfoList,
|
||||
SamplerBindingToNameMap* pSamplerBindingToNameMap) const noexcept;
|
||||
|
||||
bool getDepthWriteSet(bool* value) const noexcept;
|
||||
bool getDepthWrite(bool* value) const noexcept;
|
||||
@@ -155,6 +160,12 @@ struct ChunkUniformBlockBindings {
|
||||
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>>* uniformBlockBindings);
|
||||
};
|
||||
|
||||
struct ChunkSamplerBlockBindings {
|
||||
static bool unflatten(filaflat::Unflattener& unflattener,
|
||||
SamplerGroupBindingInfoList* pSamplerGroupBindingInfoList,
|
||||
SamplerBindingToNameMap* pSamplerBindingToNameMap);
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
#endif // TNT_FILAMENT_MATERIALPARSER_H
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#include <filament/TextureSampler.h>
|
||||
|
||||
#include <private/filament/EngineEnums.h>
|
||||
#include <private/filament/SibGenerator.h>
|
||||
#include <private/filament/SibStructs.h>
|
||||
|
||||
#include <math/mat4.h>
|
||||
|
||||
@@ -369,8 +369,8 @@ void PerViewUniforms::commit(backend::DriverApi& driver) noexcept {
|
||||
}
|
||||
|
||||
void PerViewUniforms::bind(backend::DriverApi& driver) noexcept {
|
||||
driver.bindUniformBuffer(+BindingPoints::PER_VIEW, mUniformBufferHandle);
|
||||
driver.bindSamplers(+BindingPoints::PER_VIEW, mSamplerGroupHandle);
|
||||
driver.bindUniformBuffer(+UniformBindingPoints::PER_VIEW, mUniformBufferHandle);
|
||||
driver.bindSamplers(+SamplerBindingPoints::PER_VIEW, mSamplerGroupHandle);
|
||||
}
|
||||
|
||||
void PerViewUniforms::unbindSamplers() noexcept {
|
||||
|
||||
@@ -213,8 +213,7 @@ void RenderPass::instanceify() noexcept {
|
||||
uint32_t instancedPrimitiveOffset = 0;
|
||||
|
||||
// TODO: for the case of instancing we could actually use 128 instead of 64 instances
|
||||
constexpr size_t maxInstanceCount = sizeof(PerRenderableUib) / sizeof(PerRenderableData);
|
||||
static_assert(maxInstanceCount == 64); // just to make sure we don't change by accident
|
||||
constexpr size_t maxInstanceCount = CONFIG_MAX_INSTANCES;
|
||||
|
||||
while (curr != last) {
|
||||
|
||||
@@ -761,28 +760,28 @@ void RenderPass::Executor::recordDriverCommands(FEngine& engine, backend::Driver
|
||||
|
||||
// bind per-renderable uniform block. there is no need to attempt to skip this command
|
||||
// because the backends already do this.
|
||||
driver.bindUniformBufferRange(+BindingPoints::PER_RENDERABLE,
|
||||
driver.bindUniformBufferRange(+UniformBindingPoints::PER_RENDERABLE,
|
||||
(info.instanceCount > 1) ? mInstancedUboHandle : uboHandle,
|
||||
info.index * sizeof(PerRenderableData),
|
||||
sizeof(PerRenderableUib));
|
||||
|
||||
if (UTILS_UNLIKELY(info.skinningHandle)) {
|
||||
// note: we can't bind less than sizeof(PerRenderableBoneUib) due to glsl limitations
|
||||
driver.bindUniformBufferRange(+BindingPoints::PER_RENDERABLE_BONES,
|
||||
driver.bindUniformBufferRange(+UniformBindingPoints::PER_RENDERABLE_BONES,
|
||||
info.skinningHandle,
|
||||
info.skinningOffset * sizeof(PerRenderableBoneUib::BoneData),
|
||||
sizeof(PerRenderableBoneUib));
|
||||
// note: even if only skinning is enabled, binding morphTargetBuffer is needed.
|
||||
driver.bindSamplers(+BindingPoints::PER_RENDERABLE_MORPHING,
|
||||
driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_MORPHING,
|
||||
info.morphTargetBuffer);
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY(info.morphWeightBuffer)) {
|
||||
// Instead of using a UBO per primitive, we could also have a single UBO for all
|
||||
// primitives and use bindUniformBufferRange which might be more efficient.
|
||||
driver.bindUniformBuffer(+BindingPoints::PER_RENDERABLE_MORPHING,
|
||||
driver.bindUniformBuffer(+UniformBindingPoints::PER_RENDERABLE_MORPHING,
|
||||
info.morphWeightBuffer);
|
||||
driver.bindSamplers(+BindingPoints::PER_RENDERABLE_MORPHING,
|
||||
driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_MORPHING,
|
||||
info.morphTargetBuffer);
|
||||
}
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ void ShadowMap::updateDirectional(const FScene::LightSoa& lightData, size_t inde
|
||||
}
|
||||
}
|
||||
|
||||
mHasVisibleShadows = vertexCount >= 2;
|
||||
mHasVisibleShadows = vertexCount >= 4;
|
||||
if (mHasVisibleShadows) {
|
||||
// We can't use LISPSM in stable mode
|
||||
const bool USE_LISPSM = ENABLE_LISPSM && mEngine.debug.shadowmap.lispsm && !params.options.stable;
|
||||
@@ -613,6 +613,7 @@ mat4f ShadowMap::computeVsmLightSpaceMatrix(const mat4f& lightSpacePcf,
|
||||
// This construct a frustum (similar to glFrustum or frustum), except
|
||||
// it looks towards the +y axis, and assumes -1,1 for the left/right and bottom/top planes.
|
||||
mat4f ShadowMap::warpFrustum(float n, float f) noexcept {
|
||||
assert_invariant(f > n);
|
||||
const float d = 1 / (f - n);
|
||||
const float A = (f + n) * d;
|
||||
const float B = -2 * n * f * d;
|
||||
@@ -835,6 +836,23 @@ size_t ShadowMap::intersectFrustumWithBox(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* At this point it's possible to have zero vertices (i.e.: no vertices of the box
|
||||
* are in the frustum and no vertices of the frustum are in the box) but both objects
|
||||
* have a non-null intersection, e.g.:
|
||||
*
|
||||
* +--------+
|
||||
* | |
|
||||
* +----|--------|---+
|
||||
* | | | |
|
||||
* | | | |
|
||||
* | | | |
|
||||
* +----|--------|---+
|
||||
* | |
|
||||
* +--------+
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* It's not enough here to have all 8 vertices, consider this:
|
||||
*
|
||||
@@ -859,7 +877,9 @@ size_t ShadowMap::intersectFrustumWithBox(
|
||||
* However, a special case is if all the vertices of the box are inside the frustum.
|
||||
*/
|
||||
|
||||
if (someFrustumVerticesAreInTheBox || vertexCount < 8) {
|
||||
if (someFrustumVerticesAreInTheBox && vertexCount >= 8) {
|
||||
// special case, we don't need to calculate any edge intersections
|
||||
} else {
|
||||
// c) intersect scene's volume edges with frustum planes
|
||||
vertexCount = intersectFrustum(outVertices.data(), vertexCount,
|
||||
wsSceneReceiversCorners.vertices, wsFrustumCorners);
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
#include "details/Texture.h"
|
||||
#include "details/Material.h"
|
||||
|
||||
#include "private/filament/SibGenerator.h"
|
||||
#include <private/filament/SibStructs.h>
|
||||
|
||||
#include "filament/RenderableManager.h"
|
||||
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
#include "details/VertexBuffer.h"
|
||||
#include "details/View.h"
|
||||
|
||||
#include <private/filament/SibGenerator.h>
|
||||
#include <private/filament/SibStructs.h>
|
||||
|
||||
#include <filament/MaterialEnums.h>
|
||||
|
||||
|
||||
@@ -16,24 +16,20 @@
|
||||
|
||||
#include "details/Material.h"
|
||||
|
||||
#include "DFG.h"
|
||||
#include "MaterialParser.h"
|
||||
|
||||
#include "details/Engine.h"
|
||||
|
||||
#include "FilamentAPI-impl.h"
|
||||
|
||||
#include <private/filament/SibGenerator.h>
|
||||
#include <private/filament/UibStructs.h>
|
||||
#include <private/filament/Variant.h>
|
||||
|
||||
#include <private/filament/EngineEnums.h>
|
||||
#include <private/filament/SamplerInterfaceBlock.h>
|
||||
#include <private/filament/UniformInterfaceBlock.h>
|
||||
|
||||
#include "backend/Program.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Program.h>
|
||||
|
||||
#include <utils/CString.h>
|
||||
#include <utils/Panic.h>
|
||||
@@ -127,24 +123,6 @@ Material* Material::Builder::build(Engine& engine) {
|
||||
return upcast(engine).createMaterial(*this);
|
||||
}
|
||||
|
||||
static void addSamplerGroup(Program& pb, BindingPoints bindingPoint,
|
||||
SamplerInterfaceBlock const& sib, SamplerBindingMap const& map) {
|
||||
const size_t samplerCount = sib.getSize();
|
||||
if (samplerCount) {
|
||||
std::vector<Program::Sampler> samplers(samplerCount);
|
||||
auto const& list = sib.getSamplerInfoList();
|
||||
for (size_t i = 0, c = samplerCount; i < c; ++i) {
|
||||
CString uniformName(
|
||||
SamplerInterfaceBlock::getUniformName(sib.getName().c_str(),
|
||||
list[i].name.c_str()));
|
||||
uint8_t binding = map.getSamplerBinding(bindingPoint, (uint8_t)i);
|
||||
const bool strict = (bindingPoint == filament::BindingPoints::PER_MATERIAL_INSTANCE);
|
||||
samplers[i] = { std::move(uniformName), binding, strict };
|
||||
}
|
||||
pb.setSamplerGroup(+bindingPoint, sib.getStageFlags(), samplers.data(), samplers.size());
|
||||
}
|
||||
}
|
||||
|
||||
FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
|
||||
: mEngine(engine),
|
||||
mMaterialId(engine.getMaterialId())
|
||||
@@ -176,16 +154,11 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
|
||||
|
||||
// read the uniform binding list
|
||||
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> uniformBlockBindings;
|
||||
success = parser->getUniformBlockBindings(&uniformBlockBindings);
|
||||
success = parser->getUniformBlockBindings(&mUniformBlockBindings);
|
||||
assert_invariant(success);
|
||||
|
||||
success = parser->getSamplerBlockBindings(&mSamplerGroupBindingInfoList, &mSamplerBindingToNameMap);
|
||||
assert_invariant(success);
|
||||
// store the name in an array (so we can use pointers), and create the uniform block binding
|
||||
// list for the backend (to be used with Program)
|
||||
mUniformBlockNames.reserve(uniformBlockBindings.size());
|
||||
mUniformBlockBindings.reserve(uniformBlockBindings.size());
|
||||
for (auto& item : uniformBlockBindings) {
|
||||
mUniformBlockNames.emplace_back(std::move(item.first));
|
||||
mUniformBlockBindings.emplace_back(mUniformBlockNames.back().c_str(), item.second);
|
||||
}
|
||||
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
// Register the material with matdbg.
|
||||
@@ -201,9 +174,6 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
|
||||
mSubpassInfo.isValid = false;
|
||||
}
|
||||
|
||||
// Populate sampler bindings for the backend that will consume this Material.
|
||||
mSamplerBindings.populate(&mSamplerInterfaceBlock);
|
||||
|
||||
parser->getShading(&mShading);
|
||||
parser->getMaterialProperties(&mMaterialProperties);
|
||||
parser->getBlendingMode(&mBlendingMode);
|
||||
@@ -396,25 +366,11 @@ void FMaterial::getSurfaceProgramSlow(Variant variant) const noexcept {
|
||||
Variant fragmentVariant = Variant::filterVariantFragment(variant);
|
||||
|
||||
Program pb{ getProgramBuilderWithVariants(variant, vertexVariant, fragmentVariant) };
|
||||
|
||||
// Always add the morphing sampler group because there is no way
|
||||
// that SamplerBindingMap knows the current variant.
|
||||
addSamplerGroup(pb, BindingPoints::PER_RENDERABLE_MORPHING,
|
||||
SibGenerator::getPerRenderPrimitiveMorphingSib(variant), mSamplerBindings);
|
||||
|
||||
addSamplerGroup(pb, BindingPoints::PER_VIEW, SibGenerator::getPerViewSib(variant), mSamplerBindings);
|
||||
addSamplerGroup(pb, BindingPoints::PER_MATERIAL_INSTANCE, mSamplerInterfaceBlock, mSamplerBindings);
|
||||
|
||||
// getSurfaceBindingIndexMap in GLSLPostProcessor.cpp also needs to update if sampler groups are added.
|
||||
createAndCacheProgram(std::move(pb), variant);
|
||||
}
|
||||
|
||||
void FMaterial::getPostProcessProgramSlow(Variant variant) const noexcept {
|
||||
Program pb{ getProgramBuilderWithVariants(variant, variant, variant) };
|
||||
|
||||
addSamplerGroup(pb, BindingPoints::PER_MATERIAL_INSTANCE, mSamplerInterfaceBlock, mSamplerBindings);
|
||||
|
||||
// getPostProcessBindingIndexMap in GLSLPostProcessor.cpp also needs to update if sampler groups are added.
|
||||
createAndCacheProgram(std::move(pb), variant);
|
||||
}
|
||||
|
||||
@@ -463,6 +419,26 @@ Program FMaterial::getProgramBuilderWithVariants(
|
||||
<< ", variant=(" << io::hex << variant.key << io::dec << ")";
|
||||
});
|
||||
|
||||
UTILS_NOUNROLL
|
||||
for (size_t i = 0; i < Enum::count<SamplerBindingPoints>(); i++) {
|
||||
SamplerBindingPoints bindingPoint = (SamplerBindingPoints)i;
|
||||
auto const& info = mSamplerGroupBindingInfoList[i];
|
||||
if (info.count) {
|
||||
std::array<Program::Sampler, backend::MAX_SAMPLER_COUNT> samplers{};
|
||||
for (size_t j = 0, c = info.count; j < c; ++j) {
|
||||
uint8_t binding = info.bindingOffset + j;
|
||||
samplers[j] = { mSamplerBindingToNameMap[binding], binding };
|
||||
}
|
||||
program.setSamplerGroup(+bindingPoint, info.shaderStageFlags,
|
||||
samplers.data(), info.count);
|
||||
}
|
||||
}
|
||||
|
||||
program.specializationConstants({
|
||||
{ 0, (int)mEngine.getSupportedFeatureLevel() },
|
||||
{ 1, (int)CONFIG_MAX_INSTANCES }
|
||||
});
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
#include <filament/Material.h>
|
||||
|
||||
#include <private/filament/SamplerBindingMap.h>
|
||||
#include <private/filament/SamplerBindingsInfo.h>
|
||||
#include <private/filament/SamplerInterfaceBlock.h>
|
||||
#include <private/filament/SubpassInfo.h>
|
||||
#include <private/filament/Variant.h>
|
||||
@@ -226,9 +226,9 @@ private:
|
||||
SamplerInterfaceBlock mSamplerInterfaceBlock;
|
||||
UniformInterfaceBlock mUniformInterfaceBlock;
|
||||
SubpassInfo mSubpassInfo;
|
||||
SamplerBindingMap mSamplerBindings;
|
||||
utils::FixedCapacityVector<std::pair<const char*, uint8_t>> mUniformBlockBindings;
|
||||
utils::FixedCapacityVector<utils::CString> mUniformBlockNames;
|
||||
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> mUniformBlockBindings;
|
||||
SamplerGroupBindingInfoList mSamplerGroupBindingInfoList;
|
||||
SamplerBindingToNameMap mSamplerBindingToNameMap;
|
||||
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
matdbg::MaterialKey mDebuggerId;
|
||||
|
||||
@@ -56,10 +56,10 @@ public:
|
||||
|
||||
void use(FEngine::DriverApi& driver) const {
|
||||
if (mUbHandle) {
|
||||
driver.bindUniformBuffer(+BindingPoints::PER_MATERIAL_INSTANCE, mUbHandle);
|
||||
driver.bindUniformBuffer(+UniformBindingPoints::PER_MATERIAL_INSTANCE, mUbHandle);
|
||||
}
|
||||
if (mSbHandle) {
|
||||
driver.bindSamplers(+BindingPoints::PER_MATERIAL_INSTANCE, mSbHandle);
|
||||
driver.bindSamplers(+SamplerBindingPoints::PER_MATERIAL_INSTANCE, mSbHandle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,14 +16,13 @@
|
||||
|
||||
#include "details/MorphTargetBuffer.h"
|
||||
|
||||
#include "private/filament/SibGenerator.h"
|
||||
#include <private/filament/SibStructs.h>
|
||||
|
||||
#include "details/Engine.h"
|
||||
#include <details/Engine.h>
|
||||
|
||||
#include "FilamentAPI-impl.h"
|
||||
|
||||
#include <math/mat4.h>
|
||||
|
||||
#include <math/norm.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#include <filament/TextureSampler.h>
|
||||
#include <filament/View.h>
|
||||
|
||||
#include <private/filament/SibGenerator.h>
|
||||
#include <private/filament/SibStructs.h>
|
||||
#include <private/filament/UibStructs.h>
|
||||
|
||||
#include <utils/Profiler.h>
|
||||
|
||||
@@ -453,9 +453,9 @@ private:
|
||||
|
||||
void bindPerViewUniformsAndSamplers(FEngine::DriverApi& driver) const noexcept {
|
||||
mPerViewUniforms.bind(driver);
|
||||
driver.bindUniformBuffer(+BindingPoints::LIGHTS, mLightUbh);
|
||||
driver.bindUniformBuffer(+BindingPoints::SHADOW, mShadowMapManager.getShadowUniformsHandle());
|
||||
driver.bindUniformBuffer(+BindingPoints::FROXEL_RECORDS, mFroxelizer.getRecordBuffer());
|
||||
driver.bindUniformBuffer(+UniformBindingPoints::LIGHTS, mLightUbh);
|
||||
driver.bindUniformBuffer(+UniformBindingPoints::SHADOW, mShadowMapManager.getShadowUniformsHandle());
|
||||
driver.bindUniformBuffer(+UniformBindingPoints::FROXEL_RECORDS, mFroxelizer.getRecordBuffer());
|
||||
}
|
||||
|
||||
// Clean-up the whole history, free all resources. This is typically called when the View is
|
||||
|
||||
@@ -738,13 +738,13 @@ TEST(FilamentTest, FroxelData) {
|
||||
TEST(FilamentTest, GoogleLineDirective) {
|
||||
{
|
||||
char s[512] = "#line 10 \"foobar\"";
|
||||
EXPECT_FALSE(filament::backend::requestsGoogleLineDirectivesExtension(&s[0], strlen(s)));
|
||||
EXPECT_FALSE(filament::backend::requestsGoogleLineDirectivesExtension({ &s[0], strlen(s) }));
|
||||
}
|
||||
{
|
||||
char s[512] =
|
||||
"#extension GL_GOOGLE_cpp_style_line_directive : enable\n"
|
||||
"#line 10 \"foobar\"";
|
||||
EXPECT_TRUE(filament::backend::requestsGoogleLineDirectivesExtension(&s[0], strlen(s)));
|
||||
EXPECT_TRUE(filament::backend::requestsGoogleLineDirectivesExtension({ &s[0], strlen(s) }));
|
||||
}
|
||||
{
|
||||
char s[512] =
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "Filament"
|
||||
spec.version = "1.26.0"
|
||||
spec.version = "1.27.0"
|
||||
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.26.0/filament-v1.26.0-ios.tgz" }
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.27.0/filament-v1.27.0-ios.tgz" }
|
||||
|
||||
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
|
||||
spec.pod_target_xcconfig = {
|
||||
@@ -50,13 +50,13 @@ Pod::Spec.new do |spec|
|
||||
end
|
||||
|
||||
spec.subspec "gltfio_core" do |ss|
|
||||
ss.source_files = "include/gltfio/*.h"
|
||||
ss.source_files = "include/gltfio/**/*.h"
|
||||
ss.header_mappings_dir = "include"
|
||||
ss.vendored_libraries =
|
||||
"lib/universal/libgltfio_core.a",
|
||||
"lib/universal/libdracodec.a",
|
||||
"lib/universal/libuberarchive.a",
|
||||
"lib/universal/libstb.a"
|
||||
ss.header_dir = "gltfio"
|
||||
ss.dependency "Filament/filament"
|
||||
ss.dependency "Filament/ktxreader"
|
||||
ss.dependency "Filament/uberz"
|
||||
|
||||
@@ -133,7 +133,7 @@ const float kSensitivity = 100.0f;
|
||||
NameComponentManager* ncm = new NameComponentManager(em);
|
||||
_assetLoader = AssetLoader::create({_engine, _materialProvider, ncm, &em});
|
||||
_resourceLoader = new ResourceLoader(
|
||||
{.engine = _engine, .normalizeSkinningWeights = true, .recomputeBoundingBoxes = false});
|
||||
{.engine = _engine, .normalizeSkinningWeights = true});
|
||||
_stbDecoder = createStbProvider(_engine);
|
||||
_ktxDecoder = createKtx2Provider(_engine);
|
||||
_resourceLoader->addTextureProvider("image/png", _stbDecoder);
|
||||
|
||||
@@ -141,8 +141,7 @@ void App::setupMesh() {
|
||||
|
||||
filament::gltfio::ResourceLoader({
|
||||
.engine = engine,
|
||||
.normalizeSkinningWeights = true,
|
||||
.recomputeBoundingBoxes = false
|
||||
.normalizeSkinningWeights = true
|
||||
}).loadResources(app.asset);
|
||||
|
||||
scene->addEntities(app.asset->getEntities(), app.asset->getEntityCount());
|
||||
|
||||
@@ -10,9 +10,7 @@ set(PUBLIC_HDR_DIR include)
|
||||
file(GLOB_RECURSE PUBLIC_HDRS ${PUBLIC_HDR_DIR}/**/*.h)
|
||||
|
||||
set(SRCS
|
||||
src/SamplerBindingMap.cpp
|
||||
src/SamplerInterfaceBlock.cpp
|
||||
src/SibGenerator.cpp
|
||||
src/UniformInterfaceBlock.cpp
|
||||
src/Variant.cpp
|
||||
)
|
||||
|
||||
@@ -45,7 +45,7 @@ enum UTILS_PUBLIC ChunkType : uint64_t {
|
||||
MaterialSpirv = charTo64bitNum("MAT_SPIR"),
|
||||
MaterialMetal = charTo64bitNum("MAT_METL"),
|
||||
MaterialShaderModels = charTo64bitNum("MAT_SMDL"),
|
||||
MaterialSamplerBindings = charTo64bitNum("MAT_SAMP"), // no longer used
|
||||
MaterialSamplerBindings = charTo64bitNum("MAT_SAMP"),
|
||||
MaterialUniformBindings = charTo64bitNum("MAT_UNIF"),
|
||||
MaterialProperties = charTo64bitNum("MAT_PROP"),
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
namespace filament {
|
||||
|
||||
// update this when a new version of filament wouldn't work with older materials
|
||||
static constexpr size_t MATERIAL_VERSION = 26;
|
||||
static constexpr size_t MATERIAL_VERSION = 27;
|
||||
|
||||
/**
|
||||
* Supported shading models
|
||||
|
||||
@@ -32,23 +32,31 @@ enum class PostProcessVariant : uint8_t {
|
||||
TRANSLUCENT
|
||||
};
|
||||
|
||||
// Binding points for uniform buffers and sampler buffers.
|
||||
// Effectively, these are just names.
|
||||
enum class BindingPoints : uint8_t {
|
||||
PER_VIEW = 0, // uniforms/samplers updated per view
|
||||
PER_RENDERABLE = 1, // uniforms/samplers updated per renderable
|
||||
// Binding points for uniform buffers
|
||||
enum class UniformBindingPoints : uint8_t {
|
||||
PER_VIEW = 0, // uniforms updated per view
|
||||
PER_RENDERABLE = 1, // uniforms updated per renderable
|
||||
PER_RENDERABLE_BONES = 2, // bones data, per renderable
|
||||
PER_RENDERABLE_MORPHING = 3, // morphing uniform/sampler updated per render primitive
|
||||
LIGHTS = 4, // lights data array
|
||||
SHADOW = 5, // punctual shadow data
|
||||
FROXEL_RECORDS = 6,
|
||||
PER_MATERIAL_INSTANCE = 7, // uniforms/samplers updates per material
|
||||
PER_MATERIAL_INSTANCE = 7, // uniforms updates per material
|
||||
// Update utils::Enum::count<>() below when adding values here
|
||||
// These are limited by CONFIG_BINDING_COUNT (currently 12)
|
||||
// These are limited by CONFIG_BINDING_COUNT (currently 10)
|
||||
};
|
||||
|
||||
// Binding points for sampler buffers.
|
||||
enum class SamplerBindingPoints : uint8_t {
|
||||
PER_VIEW = 0, // samplers updated per view
|
||||
PER_RENDERABLE_MORPHING = 1, // morphing sampler updated per render primitive
|
||||
PER_MATERIAL_INSTANCE = 2, // samplers updates per material
|
||||
// Update utils::Enum::count<>() below when adding values here
|
||||
// These are limited by CONFIG_SAMPLER_BINDING_COUNT (currently 4)
|
||||
};
|
||||
|
||||
// This value is limited by UBO size, ES3.0 only guarantees 16 KiB.
|
||||
// Values <= 256, use less CPU and GPU resources.
|
||||
// It's also limited by the Froxelizer's record buffer data type (uint8_t).
|
||||
constexpr size_t CONFIG_MAX_LIGHT_COUNT = 256;
|
||||
constexpr size_t CONFIG_MAX_LIGHT_INDEX = CONFIG_MAX_LIGHT_COUNT - 1;
|
||||
|
||||
@@ -61,9 +69,22 @@ constexpr size_t CONFIG_MAX_SHADOW_CASTING_SPOTS = 14;
|
||||
constexpr size_t CONFIG_MAX_SHADOW_CASCADES = 4;
|
||||
|
||||
// The maximum UBO size, in bytes. This value is set to 16 KiB due to the ES3.0 spec.
|
||||
// Note that this value constrains the maximum number of skinning bones and morph targets.
|
||||
// Note that this value constrains the maximum number of skinning bones, morph targets,
|
||||
// instances, and shadow casting spotlights.
|
||||
constexpr size_t CONFIG_MINSPEC_UBO_SIZE = 16384;
|
||||
|
||||
// The maximum number of instances that Filament automatically creates as an optimization.
|
||||
// Use a much smaller number for WebGL as a workaround for the following Chrome issues:
|
||||
// https://crbug.com/1348017 Compiling GLSL is very slow with struct arrays
|
||||
// https://crbug.com/1348363 Lighting looks wrong with D3D11 but not OpenGL
|
||||
// Note that __EMSCRIPTEN__ is not defined when running matc, but that's okay because we're
|
||||
// actually using a specification constant.
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
constexpr size_t CONFIG_MAX_INSTANCES = 8;
|
||||
#else
|
||||
constexpr size_t CONFIG_MAX_INSTANCES = 64;
|
||||
#endif
|
||||
|
||||
// The maximum number of bones that can be associated with a single renderable.
|
||||
// We store 32 bytes per bone. Must be a power-of-two, and must fit within CONFIG_MINSPEC_UBO_SIZE.
|
||||
constexpr size_t CONFIG_MAX_BONE_COUNT = 256;
|
||||
@@ -76,15 +97,16 @@ constexpr size_t CONFIG_MAX_MORPH_TARGET_COUNT = 256;
|
||||
} // namespace filament
|
||||
|
||||
template<>
|
||||
struct utils::EnableIntegerOperators<filament::BindingPoints> : public std::true_type {};
|
||||
struct utils::EnableIntegerOperators<filament::UniformBindingPoints> : public std::true_type {};
|
||||
template<>
|
||||
struct utils::EnableIntegerOperators<filament::SamplerBindingPoints> : public std::true_type {};
|
||||
|
||||
template<>
|
||||
inline constexpr size_t utils::Enum::count<filament::BindingPoints>() { return 8; }
|
||||
inline constexpr size_t utils::Enum::count<filament::UniformBindingPoints>() { return 8; }
|
||||
template<>
|
||||
inline constexpr size_t utils::Enum::count<filament::SamplerBindingPoints>() { return 3; }
|
||||
|
||||
static_assert(utils::Enum::count<filament::BindingPoints>() <= filament::backend::CONFIG_BINDING_COUNT);
|
||||
|
||||
static_assert(
|
||||
filament::BindingPoints::PER_MATERIAL_INSTANCE == utils::Enum::count<filament::BindingPoints>()-1u,
|
||||
"Dynamically sized sampler buffer must be the last binding point.");
|
||||
static_assert(utils::Enum::count<filament::UniformBindingPoints>() <= filament::backend::CONFIG_UNIFORM_BINDING_COUNT);
|
||||
static_assert(utils::Enum::count<filament::SamplerBindingPoints>() <= filament::backend::CONFIG_SAMPLER_BINDING_COUNT);
|
||||
|
||||
#endif // TNT_FILAMENT_ENGINE_ENUM_H
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_DRIVER_SAMPLERBINDINGMAP_H
|
||||
#define TNT_FILAMENT_DRIVER_SAMPLERBINDINGMAP_H
|
||||
|
||||
#include <private/filament/EngineEnums.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
class SamplerInterfaceBlock;
|
||||
|
||||
// Lookup table from (BlockIndex,LocalOffset) to (GlobalOffset,GroupIndex).
|
||||
// Also stores the mapping as a flat vector of 4-tuples to make it easy to [de]serialize.
|
||||
class SamplerBindingMap {
|
||||
public:
|
||||
// Assigns a range of finalized binding points to each sampler block.
|
||||
// If a per-material SIB is provided, then material samplers are also inserted (always at the
|
||||
// end). The optional material name is used for error reporting only.
|
||||
void populate(const SamplerInterfaceBlock* perMaterialSib = nullptr,
|
||||
const char* materialName = nullptr);
|
||||
|
||||
// Given a valid Filament binding point and an offset within the block, returns a global unique
|
||||
// binding index.
|
||||
uint8_t getSamplerBinding(BindingPoints bindingPoint, uint8_t localOffset) const {
|
||||
assert_invariant(mSamplerBlockOffsets[+bindingPoint] != UNKNOWN_OFFSET);
|
||||
return mSamplerBlockOffsets[+bindingPoint] + localOffset;
|
||||
}
|
||||
|
||||
// Gets the global offset of the first sampler in the given sampler block.
|
||||
uint8_t getBlockOffset(BindingPoints bindingPoint) const {
|
||||
assert_invariant(mSamplerBlockOffsets[+bindingPoint] != UNKNOWN_OFFSET);
|
||||
return mSamplerBlockOffsets[+bindingPoint];
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr static uint8_t UNKNOWN_OFFSET = 0xff;
|
||||
uint8_t mSamplerBlockOffsets[utils::Enum::count<BindingPoints>()] = { UNKNOWN_OFFSET };
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
#endif // TNT_FILAMENT_DRIVER_SAMPLERBINDINGMAP_H
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILABRIDGE_SAMPLERBINDINGS_INFO_H
|
||||
#define TNT_FILABRIDGE_SAMPLERBINDINGS_INFO_H
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <private/filament/EngineEnums.h>
|
||||
|
||||
#include <utils/CString.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace filament {
|
||||
|
||||
// binding information about a sampler group
|
||||
struct SamplerGroupBindingInfo {
|
||||
constexpr static uint8_t UNKNOWN_OFFSET = 0xff;
|
||||
// global binding of this block, or UNKNOWN_OFFSET if not used.
|
||||
uint8_t bindingOffset = UNKNOWN_OFFSET;
|
||||
// shader stage flags for samplers in this block
|
||||
backend::ShaderStageFlags shaderStageFlags = backend::ShaderStageFlags::NONE;
|
||||
// number of samplers in this block. Can be zero.
|
||||
uint8_t count = 0;
|
||||
};
|
||||
|
||||
// list of binding information for all known binding points
|
||||
using SamplerGroupBindingInfoList =
|
||||
std::array<SamplerGroupBindingInfo, utils::Enum::count<SamplerBindingPoints>()>;
|
||||
|
||||
// map of sampler shader binding to sampler shader name
|
||||
using SamplerBindingToNameMap =
|
||||
std::array<utils::CString, backend::MAX_SAMPLER_COUNT>;
|
||||
|
||||
} // namespace filament
|
||||
|
||||
#endif //TNT_FILABRIDGE_SAMPLERBINDINGS_INFO_H
|
||||
@@ -48,12 +48,13 @@ public:
|
||||
using SamplerParams = backend::SamplerParams;
|
||||
|
||||
struct SamplerInfo { // NOLINT(cppcoreguidelines-pro-type-member-init)
|
||||
utils::CString name; // name of this sampler
|
||||
uint8_t offset; // offset in "Sampler" of this sampler in the buffer
|
||||
Type type; // type of this sampler
|
||||
Format format; // format of this sampler
|
||||
Precision precision; // precision of this sampler
|
||||
bool multisample; // multisample capable
|
||||
utils::CString name; // name of this sampler
|
||||
utils::CString uniformName; // name of the uniform holding this sampler (needed for glsl)
|
||||
uint8_t offset; // offset in "Sampler" of this sampler in the buffer
|
||||
Type type; // type of this sampler
|
||||
Format format; // format of this sampler
|
||||
Precision precision; // precision of this sampler
|
||||
bool multisample; // multisample capable
|
||||
};
|
||||
|
||||
class Builder {
|
||||
@@ -114,11 +115,12 @@ public:
|
||||
|
||||
bool isEmpty() const noexcept { return mSamplersInfoList.empty(); }
|
||||
|
||||
static utils::CString getUniformName(const char* group, const char* sampler) noexcept;
|
||||
static utils::CString generateUniformName(const char* group, const char* sampler) noexcept;
|
||||
|
||||
private:
|
||||
friend class Builder;
|
||||
|
||||
|
||||
explicit SamplerInterfaceBlock(Builder const& builder) noexcept;
|
||||
|
||||
utils::CString mName;
|
||||
|
||||
+6
-19
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
* 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.
|
||||
@@ -14,28 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILABRIDGE_SIBGENERATOR_H
|
||||
#define TNT_FILABRIDGE_SIBGENERATOR_H
|
||||
|
||||
#include <private/filament/Variant.h>
|
||||
#ifndef TNT_FILABRIDGE_SIBSTRUCTS_H
|
||||
#define TNT_FILABRIDGE_SIBSTRUCTS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "EngineEnums.h"
|
||||
|
||||
namespace filament {
|
||||
|
||||
class SamplerInterfaceBlock;
|
||||
|
||||
class SibGenerator {
|
||||
public:
|
||||
static SamplerInterfaceBlock const& getPerViewSib(Variant variant) noexcept;
|
||||
static SamplerInterfaceBlock const& getPerRenderPrimitiveMorphingSib(Variant variant) noexcept;
|
||||
static SamplerInterfaceBlock const* getSib(filament::BindingPoints bindingPoint, Variant variant) noexcept;
|
||||
// When adding a sampler block here, make sure to also update
|
||||
// FMaterial::getSurfaceProgramSlow and FMaterial::getPostProcessProgramSlow if needed
|
||||
};
|
||||
|
||||
struct PerViewSib {
|
||||
// indices of each sampler in this SamplerInterfaceBlock (see: getPerViewSib())
|
||||
static constexpr size_t SHADOW_MAP = 0; // user defined (1024x1024) DEPTH, array
|
||||
@@ -56,5 +42,6 @@ struct PerRenderPrimitiveMorphingSib {
|
||||
static constexpr size_t SAMPLER_COUNT = 2;
|
||||
};
|
||||
|
||||
}
|
||||
#endif // TNT_FILABRIDGE_SIBGENERATOR_H
|
||||
} // namespace filament
|
||||
|
||||
#endif //TNT_FILABRIDGE_SIBSTRUCTS_H
|
||||
@@ -210,10 +210,10 @@ static_assert(sizeof(PerRenderableData) == 256,
|
||||
|
||||
struct alignas(256) PerRenderableUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
|
||||
static constexpr std::string_view _name{ "ObjectUniforms" };
|
||||
PerRenderableData data[64];
|
||||
PerRenderableData data[CONFIG_MAX_INSTANCES];
|
||||
};
|
||||
// PerRenderableUib must have an alignment of 256 to be compatible with all versions of GLES.
|
||||
static_assert(sizeof(PerRenderableUib) <= 16384,
|
||||
static_assert(sizeof(PerRenderableUib) <= CONFIG_MINSPEC_UBO_SIZE,
|
||||
"PerRenderableUib exceeds max UBO size");
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
@@ -259,7 +259,7 @@ struct ShadowUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
|
||||
};
|
||||
ShadowData shadows[CONFIG_MAX_SHADOW_CASTING_SPOTS];
|
||||
};
|
||||
static_assert(sizeof(ShadowUib) <= 16384,
|
||||
static_assert(sizeof(ShadowUib) <= CONFIG_MINSPEC_UBO_SIZE,
|
||||
"ShadowUib exceeds max UBO size");
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -43,6 +43,7 @@ public:
|
||||
backend::Precision precision{};
|
||||
std::string_view structName{};
|
||||
uint32_t stride{};
|
||||
std::string_view sizeName{};
|
||||
};
|
||||
|
||||
UniformInterfaceBlock();
|
||||
@@ -66,6 +67,7 @@ public:
|
||||
uint32_t size; // size of the array in elements, or 0 if not an array
|
||||
Precision precision; // precision of this uniform
|
||||
utils::CString structName; // name of this uniform structure if type is STRUCT
|
||||
utils::CString sizeName; // name of the size parameter in the shader
|
||||
// returns offset in bytes of this uniform (at index if an array)
|
||||
inline size_t getBufferOffset(size_t index = 0) const {
|
||||
assert_invariant(index < std::max(1u, size));
|
||||
|
||||
@@ -46,7 +46,7 @@ SamplerInterfaceBlock::Builder& SamplerInterfaceBlock::Builder::add(
|
||||
std::string_view samplerName, Type type, Format format,
|
||||
Precision precision, bool multisample) noexcept {
|
||||
mEntries.push_back({
|
||||
{ samplerName.data(), samplerName.size() },
|
||||
{ samplerName.data(), samplerName.size() }, { },
|
||||
uint8_t(mEntries.size()), type, format, precision, multisample });
|
||||
return *this;
|
||||
}
|
||||
@@ -84,6 +84,7 @@ SamplerInterfaceBlock::SamplerInterfaceBlock(Builder const& builder) noexcept
|
||||
assert_invariant(i == e.offset);
|
||||
SamplerInfo& info = samplersInfoList[i++];
|
||||
info = e;
|
||||
info.uniformName = generateUniformName(mName.c_str(), e.name.c_str());
|
||||
infoMap[{ info.name.data(), info.name.size() }] = info.offset; // info.name.c_str() guaranteed constant
|
||||
}
|
||||
assert_invariant(i == samplersInfoList.size());
|
||||
@@ -97,7 +98,7 @@ const SamplerInterfaceBlock::SamplerInfo* SamplerInterfaceBlock::getSamplerInfo(
|
||||
return &mSamplersInfoList[pos->second];
|
||||
}
|
||||
|
||||
utils::CString SamplerInterfaceBlock::getUniformName(const char* group, const char* sampler) noexcept {
|
||||
utils::CString SamplerInterfaceBlock::generateUniformName(const char* group, const char* sampler) noexcept {
|
||||
char uniformName[256];
|
||||
|
||||
// sampler interface block name
|
||||
|
||||
@@ -41,7 +41,9 @@ UniformInterfaceBlock::Builder& UniformInterfaceBlock::Builder::add(
|
||||
mEntries.push_back({
|
||||
{ item.name.data(), item.name.size() },
|
||||
0, uint8_t(item.stride), item.type, item.size, item.precision,
|
||||
{ item.structName.data(), item.structName.size() }});
|
||||
{ item.structName.data(), item.structName.size() },
|
||||
{ item.sizeName.data(), item.sizeName.size() }
|
||||
});
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -81,7 +83,7 @@ UniformInterfaceBlock::UniformInterfaceBlock(Builder const& builder) noexcept
|
||||
offset += padding;
|
||||
|
||||
UniformInfo& info = uniformsInfoList[i];
|
||||
info = { e.name, offset, stride, e.type, e.size, e.precision, e.structName };
|
||||
info = { e.name, offset, stride, e.type, e.size, e.precision, e.structName, e.sizeName };
|
||||
|
||||
// record this uniform info
|
||||
infoMap[{ info.name.data(), info.name.size() }] = i;
|
||||
|
||||
@@ -43,6 +43,8 @@ set(COMMON_SRCS
|
||||
src/Includes.cpp
|
||||
src/MaterialBuilder.cpp
|
||||
src/MaterialVariants.cpp
|
||||
src/SamplerBindingMap.cpp
|
||||
src/SibGenerator.cpp
|
||||
src/UibGenerator.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@ public:
|
||||
/**
|
||||
* High-level hint that works in concert with TargetApi to determine the shader models (used to
|
||||
* generate GLSL) and final output representations (spirv and/or text).
|
||||
* When generating the GLSL this is used to differentiate OpenGL from OpenGLES, it is also
|
||||
* used to make some performance adjustments.
|
||||
*/
|
||||
enum class Platform {
|
||||
DESKTOP,
|
||||
@@ -60,6 +62,10 @@ public:
|
||||
ALL
|
||||
};
|
||||
|
||||
/**
|
||||
* TargetApi defines which language after transpilation will be used, it is used to
|
||||
* account for some differences between these languages when generating the GLSL.
|
||||
*/
|
||||
enum class TargetApi : uint8_t {
|
||||
OPENGL = 0x01u,
|
||||
VULKAN = 0x02u,
|
||||
@@ -67,8 +73,23 @@ public:
|
||||
ALL = OPENGL | VULKAN | METAL
|
||||
};
|
||||
|
||||
/*
|
||||
* Generally we generate GLSL that will be converted to SPIRV, optimized and then
|
||||
* transpiled to the backend's language such as MSL, ESSL300, ESSL410 or SPIRV, in this
|
||||
* case the generated GLSL uses ESSL310 or ESSL450 and has Vulkan semantics and
|
||||
* TargetLanguage::SPIRV must be used.
|
||||
*
|
||||
* However, in some cases (e.g. when no optimization is asked) we generate the *final* GLSL
|
||||
* directly, this GLSL must be ESSL300 or ESSL410 and cannot use any Vulkan syntax, for this
|
||||
* situation we use TargetLanguage::GLSL. In this case TargetApi is guaranteed to be OPENGL.
|
||||
*
|
||||
* Note that TargetLanguage::GLSL is not the common case, as it is generally note used in
|
||||
* release builds.
|
||||
*
|
||||
* Also note that glslang performs semantics analysis on whichever GLSL ends up being generated.
|
||||
*/
|
||||
enum class TargetLanguage {
|
||||
GLSL, // GLSL with OpenGL semantics
|
||||
GLSL, // GLSL with OpenGL 4.1 / OpenGL ES 3.0 semantics
|
||||
SPIRV // GLSL with Vulkan semantics
|
||||
};
|
||||
|
||||
@@ -106,7 +127,7 @@ protected:
|
||||
bool mGenerateDebugInfo = false;
|
||||
utils::bitset32 mShaderModels;
|
||||
struct CodeGenParams {
|
||||
int shaderModel;
|
||||
ShaderModel shaderModel;
|
||||
TargetApi targetApi;
|
||||
TargetLanguage targetLanguage;
|
||||
};
|
||||
@@ -114,7 +135,7 @@ protected:
|
||||
// For finding properties and running semantic analysis, we always use the same code gen
|
||||
// permutation. This is the first permutation generated with default arguments passed to matc.
|
||||
const CodeGenParams mSemanticCodeGenParams = {
|
||||
.shaderModel = (int) ShaderModel::MOBILE,
|
||||
.shaderModel = ShaderModel::MOBILE,
|
||||
.targetApi = TargetApi::OPENGL,
|
||||
.targetLanguage = TargetLanguage::SPIRV
|
||||
};
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
#include <filament/MaterialEnums.h>
|
||||
#include <private/filament/Variant.h>
|
||||
#include <private/filament/SibGenerator.h>
|
||||
#include "SibGenerator.h"
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
@@ -55,15 +55,10 @@ UTILS_NOINLINE
|
||||
static void generateBindingIndexMap(const GLSLPostProcessor::Config& config,
|
||||
SamplerInterfaceBlock const& sib, BindingIndexMap& map) {
|
||||
const auto stageFlags = sib.getStageFlags();
|
||||
|
||||
|
||||
|
||||
if (hasShaderType(stageFlags, config.shaderType)) {
|
||||
const auto& infoList = sib.getSamplerInfoList();
|
||||
for (const auto& info: infoList) {
|
||||
auto uniformName = SamplerInterfaceBlock::getUniformName(
|
||||
sib.getName().c_str(), info.name.c_str());
|
||||
map[uniformName.c_str()] = map.size();
|
||||
map[info.uniformName.c_str()] = map.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,9 +68,9 @@ static BindingIndexMap getBindingIndexMap(const GLSLPostProcessor::Config& confi
|
||||
switch (config.domain) {
|
||||
case MaterialDomain::SURFACE:
|
||||
UTILS_NOUNROLL
|
||||
for (size_t blockIndex = 0; blockIndex < Enum::count<BindingPoints>(); blockIndex++) {
|
||||
if (blockIndex != BindingPoints::PER_MATERIAL_INSTANCE) {
|
||||
auto const* sib = SibGenerator::getSib(BindingPoints(blockIndex), config.variant);
|
||||
for (size_t blockIndex = 0; blockIndex < Enum::count<SamplerBindingPoints>(); blockIndex++) {
|
||||
if (blockIndex != SamplerBindingPoints::PER_MATERIAL_INSTANCE) {
|
||||
auto const* sib = SibGenerator::getSib(SamplerBindingPoints(blockIndex), config.variant);
|
||||
if (sib) {
|
||||
generateBindingIndexMap(config, *sib, map);
|
||||
}
|
||||
@@ -220,14 +215,9 @@ void GLSLPostProcessor::spirvToToMsl(const SpirvBlob *spirv, std::string *outMsl
|
||||
|
||||
bool GLSLPostProcessor::process(const std::string& inputShader, Config const& config,
|
||||
std::string* outputGlsl, SpirvBlob* outputSpirv, std::string* outputMsl) {
|
||||
using TargetLanguage = MaterialBuilder::TargetLanguage;
|
||||
|
||||
// If TargetApi is Vulkan, then we need post-processing even if there's no optimization.
|
||||
// If we're using framebuffer fetch, we also need to force our compilation target to Vulkan, as
|
||||
// it is only supported in GLSL for Vulkan.
|
||||
using TargetApi = MaterialBuilder::TargetApi;
|
||||
const TargetApi targetApi = (outputSpirv || config.hasFramebufferFetch) ? TargetApi::VULKAN :
|
||||
TargetApi::OPENGL;
|
||||
if (targetApi == TargetApi::OPENGL && mOptimization == MaterialBuilder::Optimization::NONE) {
|
||||
if (config.targetLanguage == TargetLanguage::GLSL) {
|
||||
*outputGlsl = inputShader;
|
||||
if (mPrintShaders) {
|
||||
slog.i << *outputGlsl << io::endl;
|
||||
@@ -235,16 +225,19 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
|
||||
return true;
|
||||
}
|
||||
|
||||
InternalConfig internalConfig;
|
||||
InternalConfig internalConfig{
|
||||
.glslOutput = outputGlsl,
|
||||
.spirvOutput = outputSpirv,
|
||||
.mslOutput = outputMsl,
|
||||
};
|
||||
|
||||
internalConfig.glslOutput = outputGlsl;
|
||||
internalConfig.spirvOutput = outputSpirv;
|
||||
internalConfig.mslOutput = outputMsl;
|
||||
|
||||
if (config.shaderType == backend::VERTEX) {
|
||||
internalConfig.shLang = EShLangVertex;
|
||||
} else {
|
||||
internalConfig.shLang = EShLangFragment;
|
||||
switch (config.shaderType) {
|
||||
case VERTEX:
|
||||
internalConfig.shLang = EShLangVertex;
|
||||
break;
|
||||
case FRAGMENT:
|
||||
internalConfig.shLang = EShLangFragment;
|
||||
break;
|
||||
}
|
||||
|
||||
TProgram program;
|
||||
@@ -257,9 +250,18 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
|
||||
tShader.setStrings(&shaderCString, 1);
|
||||
|
||||
internalConfig.langVersion = GLSLTools::glslangVersionFromShaderModel(config.shaderModel);
|
||||
GLSLTools::prepareShaderParser(targetApi, tShader, internalConfig.shLang,
|
||||
internalConfig.langVersion, mOptimization);
|
||||
EShMessages msg = GLSLTools::glslangFlagsFromTargetApi(targetApi);
|
||||
GLSLTools::prepareShaderParser(config.targetApi, config.targetLanguage, tShader,
|
||||
internalConfig.shLang, internalConfig.langVersion);
|
||||
|
||||
EShMessages msg = GLSLTools::glslangFlagsFromTargetApi(config.targetApi, config.targetLanguage);
|
||||
if (config.hasFramebufferFetch) {
|
||||
// FIXME: subpasses require EShMsgVulkanRules, which I think is a mistake.
|
||||
// SpvRules should be enough.
|
||||
// I think this could cause the compilation to fail on gl_VertexID.
|
||||
using Type = std::underlying_type_t<EShMessages>;
|
||||
msg = EShMessages(Type(msg) | Type(EShMessages::EShMsgVulkanRules));
|
||||
}
|
||||
|
||||
bool ok = tShader.parse(&DefaultTBuiltInResource, internalConfig.langVersion, false, msg);
|
||||
if (!ok) {
|
||||
slog.e << tShader.getInfoLog() << io::endl;
|
||||
@@ -325,15 +327,14 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
|
||||
|
||||
void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
|
||||
GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const {
|
||||
|
||||
using TargetApi = MaterialBuilder::TargetApi;
|
||||
assert_invariant(bool(internalConfig.spirvOutput) == (config.targetApi != TargetApi::OPENGL));
|
||||
|
||||
std::string glsl;
|
||||
TShader::ForbidIncluder forbidIncluder;
|
||||
|
||||
int version = GLSLTools::glslangVersionFromShaderModel(config.shaderModel);
|
||||
const TargetApi targetApi = internalConfig.spirvOutput ? TargetApi::VULKAN : TargetApi::OPENGL;
|
||||
EShMessages msg = GLSLTools::glslangFlagsFromTargetApi(targetApi);
|
||||
const int version = GLSLTools::glslangVersionFromShaderModel(config.shaderModel);
|
||||
EShMessages msg = GLSLTools::glslangFlagsFromTargetApi(config.targetApi, config.targetLanguage);
|
||||
bool ok = tShader.preprocess(&DefaultTBuiltInResource, version, ENoProfile, false, false,
|
||||
msg, &glsl, forbidIncluder);
|
||||
|
||||
@@ -351,8 +352,8 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
|
||||
|
||||
const char* shaderCString = glsl.c_str();
|
||||
spirvShader.setStrings(&shaderCString, 1);
|
||||
GLSLTools::prepareShaderParser(targetApi, spirvShader,
|
||||
internalConfig.shLang, internalConfig.langVersion, mOptimization);
|
||||
GLSLTools::prepareShaderParser(config.targetApi, config.targetLanguage, spirvShader,
|
||||
internalConfig.shLang, internalConfig.langVersion);
|
||||
ok = spirvShader.parse(&DefaultTBuiltInResource, internalConfig.langVersion, false, msg);
|
||||
program.addShader(&spirvShader);
|
||||
// Even though we only have a single shader stage, linking is still necessary to finalize
|
||||
|
||||
@@ -52,6 +52,7 @@ public:
|
||||
struct Config {
|
||||
filament::Variant variant;
|
||||
MaterialBuilder::TargetApi targetApi;
|
||||
MaterialBuilder::TargetLanguage targetLanguage;
|
||||
filament::backend::ShaderType shaderType;
|
||||
filament::backend::ShaderModel shaderModel;
|
||||
filament::MaterialDomain domain;
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <private/filament/SamplerInterfaceBlock.h>
|
||||
#include <private/filament/UibStructs.h>
|
||||
|
||||
#include <private/filament/SibGenerator.h>
|
||||
#include "SibGenerator.h"
|
||||
|
||||
#include "MaterialVariants.h"
|
||||
|
||||
@@ -97,19 +97,19 @@ void MaterialBuilderBase::prepare(bool vulkanSemantics) {
|
||||
|
||||
// Build a list of codegen permutations, which is useful across all types of material builders.
|
||||
static_assert(filament::backend::SHADER_MODEL_COUNT == 2);
|
||||
for (const auto shaderModel : {ShaderModel::MOBILE, ShaderModel::DESKTOP}) {
|
||||
for (const auto shaderModel: { ShaderModel::MOBILE, ShaderModel::DESKTOP }) {
|
||||
const auto i = static_cast<uint8_t>(shaderModel);
|
||||
if (!mShaderModels.test(i)) {
|
||||
continue; // skip this shader model since it was not requested.
|
||||
}
|
||||
if (any(mTargetApi & TargetApi::OPENGL)) {
|
||||
mCodeGenPermutations.push_back({i, TargetApi::OPENGL, glTargetLanguage});
|
||||
mCodeGenPermutations.push_back({shaderModel, TargetApi::OPENGL, glTargetLanguage});
|
||||
}
|
||||
if (any(mTargetApi & TargetApi::VULKAN)) {
|
||||
mCodeGenPermutations.push_back({i, TargetApi::VULKAN, TargetLanguage::SPIRV});
|
||||
mCodeGenPermutations.push_back({shaderModel, TargetApi::VULKAN, TargetLanguage::SPIRV});
|
||||
}
|
||||
if (any(mTargetApi & TargetApi::METAL)) {
|
||||
mCodeGenPermutations.push_back({i, TargetApi::METAL, TargetLanguage::SPIRV});
|
||||
mCodeGenPermutations.push_back({shaderModel, TargetApi::METAL, TargetLanguage::SPIRV});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -517,8 +517,9 @@ bool MaterialBuilder::findProperties(filament::backend::ShaderType type,
|
||||
std::string shaderCodeAllProperties = peek(type, mSemanticCodeGenParams, allProperties);
|
||||
// Populate mProperties with the properties set in the shader.
|
||||
if (!glslTools.findProperties(type, shaderCodeAllProperties, mProperties,
|
||||
mSemanticCodeGenParams.targetApi,
|
||||
ShaderModel(mSemanticCodeGenParams.shaderModel))) {
|
||||
mSemanticCodeGenParams.targetApi,
|
||||
mSemanticCodeGenParams.targetLanguage,
|
||||
mSemanticCodeGenParams.shaderModel)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -561,6 +562,7 @@ bool MaterialBuilder::runSemanticAnalysis(MaterialInfo const& info) noexcept {
|
||||
GLSLTools glslTools;
|
||||
|
||||
TargetApi targetApi = mSemanticCodeGenParams.targetApi;
|
||||
TargetLanguage targetLanguage = mSemanticCodeGenParams.targetLanguage;
|
||||
assertSingleTargetApi(targetApi);
|
||||
|
||||
if (mEnableFramebufferFetch) {
|
||||
@@ -570,12 +572,13 @@ bool MaterialBuilder::runSemanticAnalysis(MaterialInfo const& info) noexcept {
|
||||
|
||||
ShaderModel model = static_cast<ShaderModel>(mSemanticCodeGenParams.shaderModel);
|
||||
std::string shaderCode = peek(ShaderType::VERTEX, mSemanticCodeGenParams, mProperties);
|
||||
bool result = glslTools.analyzeVertexShader(shaderCode, model, mMaterialDomain, targetApi, info);
|
||||
bool result = glslTools.analyzeVertexShader(shaderCode, model, mMaterialDomain,
|
||||
targetApi, targetLanguage, info);
|
||||
if (!result) return false;
|
||||
|
||||
shaderCode = peek(ShaderType::FRAGMENT, mSemanticCodeGenParams, mProperties);
|
||||
result = glslTools.analyzeFragmentShader(shaderCode, model, mMaterialDomain, targetApi,
|
||||
mCustomSurfaceShading, info);
|
||||
result = glslTools.analyzeFragmentShader(shaderCode, model, mMaterialDomain,
|
||||
targetApi, targetLanguage, mCustomSurfaceShading, info);
|
||||
return result;
|
||||
#else
|
||||
return true;
|
||||
@@ -747,6 +750,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
|
||||
GLSLPostProcessor::Config config{
|
||||
.variant = v.variant,
|
||||
.targetApi = targetApi,
|
||||
.targetLanguage = targetLanguage,
|
||||
.shaderType = v.stage,
|
||||
.shaderModel = shaderModel,
|
||||
.domain = mMaterialDomain,
|
||||
@@ -972,7 +976,7 @@ Package MaterialBuilder::build(JobSystem& jobSystem) noexcept {
|
||||
return Package::invalidPackage();
|
||||
}
|
||||
|
||||
info.samplerBindings.populate(&info.sib, mMaterialName.c_str());
|
||||
info.samplerBindings.init(mMaterialDomain, &info.sib, mMaterialName.c_str());
|
||||
|
||||
// Create chunk tree.
|
||||
ChunkContainer container;
|
||||
@@ -1030,7 +1034,7 @@ std::string MaterialBuilder::peek(filament::backend::ShaderType type,
|
||||
|
||||
MaterialInfo info;
|
||||
prepareToBuild(info);
|
||||
info.samplerBindings.populate(&info.sib, mMaterialName.c_str());
|
||||
info.samplerBindings.init(mMaterialDomain, &info.sib, mMaterialName.c_str());
|
||||
|
||||
if (type == filament::backend::ShaderType::VERTEX) {
|
||||
return sg.createVertexProgram(ShaderModel(params.shaderModel),
|
||||
@@ -1047,35 +1051,37 @@ void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo&
|
||||
container.addSimpleChild<const char*>(ChunkType::MaterialName, mMaterialName.c_str_safe());
|
||||
container.addSimpleChild<uint32_t>(ChunkType::MaterialShaderModels, mShaderModels.getValue());
|
||||
container.addSimpleChild<uint8_t>(ChunkType::MaterialDomain, static_cast<uint8_t>(mMaterialDomain));
|
||||
container.addSimpleChild<uint8_t>(ChunkType::MaterialRefraction, static_cast<uint8_t>(mRefractionMode));
|
||||
container.addSimpleChild<uint8_t>(ChunkType::MaterialRefractionType, static_cast<uint8_t>(mRefractionType));
|
||||
|
||||
// note: this chunk is only needed for OpenGL backends, which don't all support layout(binding=)
|
||||
using namespace filament;
|
||||
utils::FixedCapacityVector<std::pair<std::string_view, BindingPoints>> list = {
|
||||
{ PerViewUib::_name, BindingPoints::PER_VIEW },
|
||||
{ PerRenderableUib::_name, BindingPoints::PER_RENDERABLE },
|
||||
{ LightsUib::_name, BindingPoints::LIGHTS },
|
||||
{ ShadowUib::_name, BindingPoints::SHADOW },
|
||||
{ FroxelRecordUib::_name, BindingPoints::FROXEL_RECORDS },
|
||||
{ PerRenderableBoneUib::_name, BindingPoints::PER_RENDERABLE_BONES },
|
||||
{ PerRenderableMorphingUib::_name, BindingPoints::PER_RENDERABLE_MORPHING },
|
||||
{ info.uib.getName(), BindingPoints::PER_MATERIAL_INSTANCE }
|
||||
utils::FixedCapacityVector<std::pair<std::string_view, UniformBindingPoints>> list = {
|
||||
{ PerViewUib::_name, UniformBindingPoints::PER_VIEW },
|
||||
{ PerRenderableUib::_name, UniformBindingPoints::PER_RENDERABLE },
|
||||
{ LightsUib::_name, UniformBindingPoints::LIGHTS },
|
||||
{ ShadowUib::_name, UniformBindingPoints::SHADOW },
|
||||
{ FroxelRecordUib::_name, UniformBindingPoints::FROXEL_RECORDS },
|
||||
{ PerRenderableBoneUib::_name, UniformBindingPoints::PER_RENDERABLE_BONES },
|
||||
{ PerRenderableMorphingUib::_name, UniformBindingPoints::PER_RENDERABLE_MORPHING },
|
||||
{ info.uib.getName(), UniformBindingPoints::PER_MATERIAL_INSTANCE }
|
||||
};
|
||||
container.addChild<MaterialUniformBlockBindingsChunk>(std::move(list));
|
||||
|
||||
// UIB
|
||||
// note: this chunk is needed for Vulkan and GL backends. Metal shouldn't need it (but
|
||||
// still does as of now).
|
||||
container.addChild<MaterialSamplerBlockBindingChunk>(info.samplerBindings);
|
||||
|
||||
// User Material UIB
|
||||
container.addChild<MaterialUniformInterfaceBlockChunk>(info.uib);
|
||||
|
||||
// SIB
|
||||
// User Material SIB
|
||||
container.addChild<MaterialSamplerInterfaceBlockChunk>(info.sib);
|
||||
|
||||
// Subpass
|
||||
// User Subpass
|
||||
container.addChild<MaterialSubpassInterfaceBlockChunk>(info.subpass);
|
||||
|
||||
|
||||
container.addSimpleChild<bool>(ChunkType::MaterialDoubleSidedSet, mDoubleSidedCapability);
|
||||
container.addSimpleChild<bool>(ChunkType::MaterialDoubleSided, mDoubleSided);
|
||||
|
||||
container.addSimpleChild<uint8_t>(ChunkType::MaterialBlendingMode, static_cast<uint8_t>(mBlendingMode));
|
||||
container.addSimpleChild<uint8_t>(ChunkType::MaterialTransparencyMode, static_cast<uint8_t>(mTransparencyMode));
|
||||
container.addSimpleChild<uint8_t>(ChunkType::MaterialReflectionMode, static_cast<uint8_t>(mReflectionMode));
|
||||
@@ -1087,6 +1093,7 @@ void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo&
|
||||
container.addSimpleChild<uint8_t>(ChunkType::MaterialCullingMode, static_cast<uint8_t>(mCullingMode));
|
||||
|
||||
uint64_t properties = 0;
|
||||
UTILS_NOUNROLL
|
||||
for (size_t i = 0; i < MATERIAL_PROPERTIES_COUNT; i++) {
|
||||
if (mProperties[i]) {
|
||||
properties |= uint64_t(1u) << i;
|
||||
@@ -1106,6 +1113,8 @@ void MaterialBuilder::writeSurfaceChunks(ChunkContainer& container) const noexce
|
||||
container.addSimpleChild<bool>(ChunkType::MaterialShadowMultiplier, mShadowMultiplier);
|
||||
}
|
||||
|
||||
container.addSimpleChild<uint8_t>(ChunkType::MaterialRefraction, static_cast<uint8_t>(mRefractionMode));
|
||||
container.addSimpleChild<uint8_t>(ChunkType::MaterialRefractionType, static_cast<uint8_t>(mRefractionType));
|
||||
container.addSimpleChild<bool>(ChunkType::MaterialClearCoatIorChange, mClearCoatIorChange);
|
||||
container.addSimpleChild<uint32_t>(ChunkType::MaterialRequiredAttributes, mRequiredAttributes.getValue());
|
||||
container.addSimpleChild<bool>(ChunkType::MaterialSpecularAntiAliasing, mSpecularAntiAliasing);
|
||||
|
||||
+45
-20
@@ -14,11 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "private/filament/SamplerBindingMap.h"
|
||||
#include "SamplerBindingMap.h"
|
||||
|
||||
#include "private/filament/SamplerInterfaceBlock.h"
|
||||
#include "SibGenerator.h"
|
||||
|
||||
#include <private/filament/SibGenerator.h>
|
||||
#include <filament/MaterialEnums.h>
|
||||
|
||||
#include <private/filament/SamplerInterfaceBlock.h>
|
||||
#include <private/filament/SibStructs.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
@@ -45,36 +48,58 @@ static const char* to_string(ShaderStageFlags stageFlags) noexcept {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SamplerBindingMap::populate(const SamplerInterfaceBlock* perMaterialSib,
|
||||
const char* materialName) {
|
||||
// TODO: Calculate SamplerBindingMap with a material variant.
|
||||
// The dummy variant isn't enough for calculating the binding map.
|
||||
// The material variant affects sampler bindings.
|
||||
const Variant dummyVariant{};
|
||||
void SamplerBindingMap::init(MaterialDomain materialDomain,
|
||||
const SamplerInterfaceBlock* perMaterialSib, const char* materialName) {
|
||||
|
||||
assert_invariant(mActiveSamplerCount == 0);
|
||||
|
||||
// Note: the material variant affects only the sampler types, but cannot affect
|
||||
// the actual bindings. For this reason it is okay to use the dummyVariant here.
|
||||
uint8_t offset = 0;
|
||||
size_t vertexSamplerCount = 0;
|
||||
size_t fragmentSamplerCount = 0;
|
||||
|
||||
UTILS_NOUNROLL
|
||||
for (size_t blockIndex = 0; blockIndex < Enum::count<BindingPoints>(); blockIndex++) {
|
||||
mSamplerBlockOffsets[blockIndex] = offset;
|
||||
auto processSamplerGroup = [&](SamplerBindingPoints bindingPoint){
|
||||
SamplerInterfaceBlock const* const sib =
|
||||
(blockIndex == BindingPoints::PER_MATERIAL_INSTANCE) ?
|
||||
perMaterialSib : SibGenerator::getSib(BindingPoints(blockIndex), dummyVariant);
|
||||
(bindingPoint == SamplerBindingPoints::PER_MATERIAL_INSTANCE) ?
|
||||
perMaterialSib : SibGenerator::getSib(bindingPoint, {});
|
||||
if (sib) {
|
||||
const auto& sibFields = sib->getSamplerInfoList();
|
||||
const auto stageFlags = sib->getStageFlags();
|
||||
const size_t samplerCount = sibFields.size();
|
||||
offset += samplerCount;
|
||||
auto const& list = sib->getSamplerInfoList();
|
||||
const size_t samplerCount = list.size();
|
||||
|
||||
if (any(stageFlags & ShaderStageFlags::VERTEX)) {
|
||||
vertexSamplerCount += samplerCount;
|
||||
}
|
||||
if (any(stageFlags & ShaderStageFlags::FRAGMENT)) {
|
||||
fragmentSamplerCount += samplerCount;
|
||||
}
|
||||
|
||||
mSamplerBlockOffsets[+bindingPoint] = { offset, stageFlags, uint8_t(samplerCount) };
|
||||
for (size_t i = 0; i < samplerCount; i++) {
|
||||
assert_invariant(mSamplerNamesBindingMap[offset + i].empty());
|
||||
mSamplerNamesBindingMap[offset + i] = list[i].uniformName;
|
||||
}
|
||||
|
||||
offset += samplerCount;
|
||||
}
|
||||
};
|
||||
|
||||
switch(materialDomain) {
|
||||
case MaterialDomain::SURFACE:
|
||||
UTILS_NOUNROLL
|
||||
for (size_t i = 0; i < Enum::count<SamplerBindingPoints>(); i++) {
|
||||
processSamplerGroup((SamplerBindingPoints)i);
|
||||
}
|
||||
break;
|
||||
case MaterialDomain::POST_PROCESS:
|
||||
processSamplerGroup(SamplerBindingPoints::PER_MATERIAL_INSTANCE);
|
||||
break;
|
||||
}
|
||||
|
||||
mActiveSamplerCount = offset;
|
||||
|
||||
// TODO: update this check for feature level 2
|
||||
const bool isOverflow = vertexSamplerCount > MAX_VERTEX_SAMPLER_COUNT ||
|
||||
fragmentSamplerCount > MAX_FRAGMENT_SAMPLER_COUNT;
|
||||
|
||||
@@ -90,10 +115,10 @@ void SamplerBindingMap::populate(const SamplerInterfaceBlock* perMaterialSib,
|
||||
|
||||
|
||||
UTILS_NOUNROLL
|
||||
for (size_t blockIndex = 0; blockIndex < Enum::count<BindingPoints>(); blockIndex++) {
|
||||
for (size_t blockIndex = 0; blockIndex < Enum::count<SamplerBindingPoints>(); blockIndex++) {
|
||||
SamplerInterfaceBlock const* const sib =
|
||||
(blockIndex == BindingPoints::PER_MATERIAL_INSTANCE) ?
|
||||
perMaterialSib : SibGenerator::getSib(BindingPoints(blockIndex), dummyVariant);
|
||||
(blockIndex == SamplerBindingPoints::PER_MATERIAL_INSTANCE) ?
|
||||
perMaterialSib : SibGenerator::getSib(SamplerBindingPoints(blockIndex), {});
|
||||
if (sib) {
|
||||
auto const& sibFields = sib->getSamplerInfoList();
|
||||
auto const stageFlagsAsString = to_string(sib->getStageFlags());
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_DRIVER_SAMPLERBINDINGMAP_H
|
||||
#define TNT_FILAMENT_DRIVER_SAMPLERBINDINGMAP_H
|
||||
|
||||
#include <private/filament/SamplerBindingsInfo.h>
|
||||
|
||||
#include <filament/MaterialEnums.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
class SamplerInterfaceBlock;
|
||||
|
||||
/*
|
||||
* SamplerBindingMap maps filament's (BindingPoints, offset) to a global offset.
|
||||
* This global offset is used in shaders to set the `layout(binding=` of each sampler.
|
||||
*
|
||||
* It also keeps a map of global offsets to the sampler name in the shader.
|
||||
*
|
||||
* SamplerBindingMap is flattened into the material file and used on the filament side to
|
||||
* create the backend's programs.
|
||||
*/
|
||||
class SamplerBindingMap {
|
||||
public:
|
||||
|
||||
using SamplerGroupBindingInfo = SamplerGroupBindingInfo;
|
||||
|
||||
// Initializes the SamplerBindingMap.
|
||||
// Assigns a range of finalized binding points to each sampler block.
|
||||
// If a per-material SIB is provided, then material samplers are also inserted (always at the
|
||||
// end). The optional material name is used for error reporting only.
|
||||
void init(MaterialDomain materialDomain,
|
||||
const SamplerInterfaceBlock* perMaterialSib = nullptr,
|
||||
const char* materialName = nullptr);
|
||||
|
||||
SamplerGroupBindingInfo const& getSamplerGroupBindingInfo(SamplerBindingPoints bindingPoint) const noexcept {
|
||||
return mSamplerBlockOffsets[+bindingPoint];
|
||||
}
|
||||
|
||||
// Gets the global offset of the first sampler in the given sampler block.
|
||||
inline uint8_t getBlockOffset(SamplerBindingPoints bindingPoint) const noexcept {
|
||||
assert_invariant(mSamplerBlockOffsets[+bindingPoint].bindingOffset != UNKNOWN_OFFSET);
|
||||
return getSamplerGroupBindingInfo(bindingPoint).bindingOffset;
|
||||
}
|
||||
|
||||
size_t getActiveSamplerCount() const noexcept {
|
||||
return mActiveSamplerCount;
|
||||
}
|
||||
|
||||
utils::CString const& getSamplerName(size_t binding) const noexcept {
|
||||
return mSamplerNamesBindingMap[binding];
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr static uint8_t UNKNOWN_OFFSET = SamplerGroupBindingInfo::UNKNOWN_OFFSET;
|
||||
SamplerGroupBindingInfoList mSamplerBlockOffsets{};
|
||||
SamplerBindingToNameMap mSamplerNamesBindingMap{};
|
||||
uint8_t mActiveSamplerCount = 0;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
#endif // TNT_FILAMENT_DRIVER_SAMPLERBINDINGMAP_H
|
||||
@@ -14,12 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "private/filament/SibGenerator.h"
|
||||
#include "SibGenerator.h"
|
||||
|
||||
#include "private/filament/Variant.h"
|
||||
#include "private/filament/EngineEnums.h"
|
||||
|
||||
#include <private/filament/SamplerInterfaceBlock.h>
|
||||
#include "private/filament/SamplerInterfaceBlock.h"
|
||||
#include "private/filament/SibStructs.h"
|
||||
|
||||
namespace filament {
|
||||
|
||||
@@ -97,11 +97,11 @@ SamplerInterfaceBlock const& SibGenerator::getPerRenderPrimitiveMorphingSib(Vari
|
||||
return sib;
|
||||
}
|
||||
|
||||
SamplerInterfaceBlock const* SibGenerator::getSib(BindingPoints bindingPoint, Variant variant) noexcept {
|
||||
SamplerInterfaceBlock const* SibGenerator::getSib(SamplerBindingPoints bindingPoint, Variant variant) noexcept {
|
||||
switch (bindingPoint) {
|
||||
case BindingPoints::PER_VIEW:
|
||||
case SamplerBindingPoints::PER_VIEW:
|
||||
return &getPerViewSib(variant);
|
||||
case BindingPoints::PER_RENDERABLE_MORPHING:
|
||||
case SamplerBindingPoints::PER_RENDERABLE_MORPHING:
|
||||
return &getPerRenderPrimitiveMorphingSib(variant);
|
||||
default:
|
||||
return nullptr;
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILABRIDGE_SIBGENERATOR_H
|
||||
#define TNT_FILABRIDGE_SIBGENERATOR_H
|
||||
|
||||
#include "private/filament/Variant.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "private/filament/EngineEnums.h"
|
||||
|
||||
namespace filament {
|
||||
|
||||
class SamplerInterfaceBlock;
|
||||
|
||||
class SibGenerator {
|
||||
public:
|
||||
static SamplerInterfaceBlock const& getPerViewSib(Variant variant) noexcept;
|
||||
static SamplerInterfaceBlock const& getPerRenderPrimitiveMorphingSib(Variant variant) noexcept;
|
||||
static SamplerInterfaceBlock const* getSib(filament::SamplerBindingPoints bindingPoint, Variant variant) noexcept;
|
||||
// When adding a sampler block here, make sure to also update
|
||||
// FMaterial::getSurfaceProgramSlow and FMaterial::getPostProcessProgramSlow if needed
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
#endif // TNT_FILABRIDGE_SIBGENERATOR_H
|
||||
@@ -152,8 +152,8 @@ UniformInterfaceBlock const& UibGenerator::getPerViewUib() noexcept {
|
||||
UniformInterfaceBlock const& UibGenerator::getPerRenderableUib() noexcept {
|
||||
static UniformInterfaceBlock uib = UniformInterfaceBlock::Builder()
|
||||
.name(PerRenderableUib::_name)
|
||||
.add({{ "data", 64, UniformInterfaceBlock::Type::STRUCT, {},
|
||||
"PerRenderableData", sizeof(PerRenderableData) }})
|
||||
.add({{ "data", CONFIG_MAX_INSTANCES, UniformInterfaceBlock::Type::STRUCT, {},
|
||||
"PerRenderableData", sizeof(PerRenderableData), "CONFIG_MAX_INSTANCES" }})
|
||||
.build();
|
||||
return uib;
|
||||
}
|
||||
|
||||
@@ -15,20 +15,26 @@
|
||||
*/
|
||||
#include "MaterialInterfaceBlockChunk.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "filament/MaterialChunkType.h"
|
||||
|
||||
#include "../SamplerBindingMap.h"
|
||||
#include <private/filament/SamplerInterfaceBlock.h>
|
||||
#include <private/filament/UniformInterfaceBlock.h>
|
||||
#include <private/filament/SubpassInfo.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
using namespace filament;
|
||||
|
||||
namespace filamat {
|
||||
|
||||
MaterialUniformInterfaceBlockChunk::MaterialUniformInterfaceBlockChunk(UniformInterfaceBlock& uib) :
|
||||
MaterialUniformInterfaceBlockChunk::MaterialUniformInterfaceBlockChunk(
|
||||
UniformInterfaceBlock const& uib) :
|
||||
Chunk(ChunkType::MaterialUib),
|
||||
mUib(uib){
|
||||
mUib(uib) {
|
||||
}
|
||||
|
||||
void MaterialUniformInterfaceBlockChunk::flatten(Flattener &f) {
|
||||
void MaterialUniformInterfaceBlockChunk::flatten(Flattener& f) {
|
||||
f.writeString(mUib.getName());
|
||||
auto uibFields = mUib.getUniformInfoList();
|
||||
f.writeUint64(uibFields.size());
|
||||
@@ -40,12 +46,15 @@ void MaterialUniformInterfaceBlockChunk::flatten(Flattener &f) {
|
||||
}
|
||||
}
|
||||
|
||||
MaterialSamplerInterfaceBlockChunk::MaterialSamplerInterfaceBlockChunk(SamplerInterfaceBlock& sib) :
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
MaterialSamplerInterfaceBlockChunk::MaterialSamplerInterfaceBlockChunk(
|
||||
SamplerInterfaceBlock const& sib) :
|
||||
Chunk(ChunkType::MaterialSib),
|
||||
mSib(sib) {
|
||||
}
|
||||
|
||||
void MaterialSamplerInterfaceBlockChunk::flatten(Flattener &f) {
|
||||
void MaterialSamplerInterfaceBlockChunk::flatten(Flattener& f) {
|
||||
f.writeString(mSib.getName().c_str());
|
||||
auto sibFields = mSib.getSamplerInfoList();
|
||||
f.writeUint64(sibFields.size());
|
||||
@@ -58,12 +67,14 @@ void MaterialSamplerInterfaceBlockChunk::flatten(Flattener &f) {
|
||||
}
|
||||
}
|
||||
|
||||
MaterialSubpassInterfaceBlockChunk::MaterialSubpassInterfaceBlockChunk(SubpassInfo& subpass) :
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
MaterialSubpassInterfaceBlockChunk::MaterialSubpassInterfaceBlockChunk(SubpassInfo const& subpass) :
|
||||
Chunk(ChunkType::MaterialSubpass),
|
||||
mSubpass(subpass) {
|
||||
}
|
||||
|
||||
void MaterialSubpassInterfaceBlockChunk::flatten(Flattener &f) {
|
||||
void MaterialSubpassInterfaceBlockChunk::flatten(Flattener& f) {
|
||||
f.writeString(mSubpass.block.c_str());
|
||||
f.writeUint64(mSubpass.isValid ? 1 : 0); // only ever a single subpass for now
|
||||
if (mSubpass.isValid) {
|
||||
@@ -76,8 +87,10 @@ void MaterialSubpassInterfaceBlockChunk::flatten(Flattener &f) {
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
MaterialUniformBlockBindingsChunk::MaterialUniformBlockBindingsChunk(
|
||||
utils::FixedCapacityVector<std::pair<std::string_view, filament::BindingPoints>> list)
|
||||
utils::FixedCapacityVector<std::pair<std::string_view, filament::UniformBindingPoints>> list)
|
||||
: Chunk(ChunkType::MaterialUniformBindings),
|
||||
mBindingList(std::move(list)) {
|
||||
}
|
||||
@@ -90,4 +103,36 @@ void MaterialUniformBlockBindingsChunk::flatten(Flattener& f) {
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
MaterialSamplerBlockBindingChunk::MaterialSamplerBlockBindingChunk(
|
||||
SamplerBindingMap const& samplerBindings)
|
||||
: Chunk(ChunkType::MaterialSamplerBindings),
|
||||
mSamplerBindings(samplerBindings) {
|
||||
}
|
||||
|
||||
void MaterialSamplerBlockBindingChunk::flatten(Flattener& f) {
|
||||
f.writeUint8(utils::Enum::count<SamplerBindingPoints>());
|
||||
UTILS_NOUNROLL
|
||||
for (size_t i = 0; i < utils::Enum::count<SamplerBindingPoints>(); i++) {
|
||||
SamplerBindingPoints bindingPoint = (SamplerBindingPoints)i;
|
||||
auto const& bindingInfo = mSamplerBindings.getSamplerGroupBindingInfo(bindingPoint);
|
||||
f.writeUint8(bindingInfo.bindingOffset);
|
||||
f.writeUint8((uint8_t)bindingInfo.shaderStageFlags);
|
||||
f.writeUint8(bindingInfo.count);
|
||||
}
|
||||
f.writeUint8(mSamplerBindings.getActiveSamplerCount());
|
||||
UTILS_UNUSED_IN_RELEASE size_t c = 0;
|
||||
UTILS_NOUNROLL
|
||||
for (size_t i = 0; i < backend::MAX_SAMPLER_COUNT; i++) {
|
||||
auto const& uniformName = mSamplerBindings.getSamplerName(i);
|
||||
if (!uniformName.empty()) {
|
||||
f.writeUint8((uint8_t)i);
|
||||
f.writeString(uniformName.c_str());
|
||||
c++;
|
||||
}
|
||||
}
|
||||
assert_invariant(c == mSamplerBindings.getActiveSamplerCount());
|
||||
}
|
||||
|
||||
} // namespace filamat
|
||||
|
||||
@@ -20,55 +20,80 @@
|
||||
#include "Chunk.h"
|
||||
|
||||
#include <private/filament/EngineEnums.h>
|
||||
#include <private/filament/SamplerInterfaceBlock.h>
|
||||
#include <private/filament/UniformInterfaceBlock.h>
|
||||
#include <private/filament/SubpassInfo.h>
|
||||
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
namespace filament {
|
||||
class SamplerBindingMap;
|
||||
class SamplerInterfaceBlock;
|
||||
class UniformInterfaceBlock;
|
||||
struct SubpassInfo;
|
||||
} // namespace filament
|
||||
|
||||
namespace filamat {
|
||||
|
||||
class MaterialUniformInterfaceBlockChunk final : public Chunk {
|
||||
public:
|
||||
explicit MaterialUniformInterfaceBlockChunk(filament::UniformInterfaceBlock& uib);
|
||||
explicit MaterialUniformInterfaceBlockChunk(filament::UniformInterfaceBlock const& uib);
|
||||
~MaterialUniformInterfaceBlockChunk() final = default;
|
||||
|
||||
private:
|
||||
void flatten(Flattener &) final;
|
||||
void flatten(Flattener&) final;
|
||||
|
||||
filament::UniformInterfaceBlock& mUib;
|
||||
filament::UniformInterfaceBlock const& mUib;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
class MaterialSamplerInterfaceBlockChunk final : public Chunk {
|
||||
public:
|
||||
explicit MaterialSamplerInterfaceBlockChunk(filament::SamplerInterfaceBlock& sib);
|
||||
explicit MaterialSamplerInterfaceBlockChunk(filament::SamplerInterfaceBlock const& sib);
|
||||
~MaterialSamplerInterfaceBlockChunk() final = default;
|
||||
|
||||
private:
|
||||
void flatten(Flattener &) final;
|
||||
void flatten(Flattener&) final;
|
||||
|
||||
filament::SamplerInterfaceBlock& mSib;
|
||||
filament::SamplerInterfaceBlock const& mSib;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
class MaterialSubpassInterfaceBlockChunk final : public Chunk {
|
||||
public:
|
||||
explicit MaterialSubpassInterfaceBlockChunk(filament::SubpassInfo& subpass);
|
||||
explicit MaterialSubpassInterfaceBlockChunk(filament::SubpassInfo const& subpass);
|
||||
~MaterialSubpassInterfaceBlockChunk() final = default;
|
||||
|
||||
private:
|
||||
void flatten(Flattener &) final;
|
||||
void flatten(Flattener&) final;
|
||||
|
||||
filament::SubpassInfo& mSubpass;
|
||||
filament::SubpassInfo const& mSubpass;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
class MaterialUniformBlockBindingsChunk final : public Chunk {
|
||||
public:
|
||||
explicit MaterialUniformBlockBindingsChunk(
|
||||
utils::FixedCapacityVector<std::pair<std::string_view, filament::BindingPoints>> list);
|
||||
utils::FixedCapacityVector<std::pair<std::string_view, filament::UniformBindingPoints>> list);
|
||||
~MaterialUniformBlockBindingsChunk() final = default;
|
||||
|
||||
private:
|
||||
void flatten(Flattener&) final;
|
||||
|
||||
utils::FixedCapacityVector<std::pair<std::string_view, filament::UniformBindingPoints>> mBindingList;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
class MaterialSamplerBlockBindingChunk final : public Chunk {
|
||||
public:
|
||||
explicit MaterialSamplerBlockBindingChunk(filament::SamplerBindingMap const& samplerBindings);
|
||||
~MaterialSamplerBlockBindingChunk() final = default;
|
||||
|
||||
private:
|
||||
void flatten(Flattener &) final;
|
||||
|
||||
utils::FixedCapacityVector<std::pair<std::string_view, filament::BindingPoints>> mBindingList;
|
||||
filament::SamplerBindingMap const& mSamplerBindings;
|
||||
};
|
||||
|
||||
} // namespace filamat
|
||||
|
||||
@@ -16,11 +16,7 @@
|
||||
|
||||
#include "GLSLTools.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <filament/MaterialEnums.h>
|
||||
#include <private/filament/UniformInterfaceBlock.h>
|
||||
#include <private/filament/SamplerInterfaceBlock.h>
|
||||
#include <filamat/MaterialBuilder.h>
|
||||
#include "../shaders/MaterialInfo.h"
|
||||
|
||||
@@ -52,10 +48,10 @@ GLSLangCleaner::~GLSLangCleaner() {
|
||||
SetThreadPoolAllocator(mAllocator);
|
||||
}
|
||||
|
||||
bool GLSLTools::analyzeFragmentShader(const std::string& shaderCode, ShaderModel model,
|
||||
MaterialBuilder::MaterialDomain materialDomain,
|
||||
MaterialBuilder::TargetApi targetApi, bool hasCustomSurfaceShading,
|
||||
MaterialInfo const& info) const noexcept {
|
||||
bool GLSLTools::analyzeFragmentShader(const std::string& shaderCode,
|
||||
filament::backend::ShaderModel model, MaterialBuilder::MaterialDomain materialDomain,
|
||||
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
|
||||
bool hasCustomSurfaceShading, MaterialInfo const& info) noexcept {
|
||||
|
||||
// Parse to check syntax and semantic.
|
||||
const char* shaderCString = shaderCode.c_str();
|
||||
@@ -65,7 +61,7 @@ bool GLSLTools::analyzeFragmentShader(const std::string& shaderCode, ShaderModel
|
||||
|
||||
GLSLangCleaner cleaner;
|
||||
int version = glslangVersionFromShaderModel(model);
|
||||
EShMessages msg = glslangFlagsFromTargetApi(targetApi);
|
||||
EShMessages msg = glslangFlagsFromTargetApi(targetApi, targetLanguage);
|
||||
bool ok = tShader.parse(&DefaultTBuiltInResource, version, false, msg);
|
||||
if (!ok) {
|
||||
utils::slog.e << "ERROR: Unable to parse fragment shader:" << utils::io::endl;
|
||||
@@ -141,10 +137,10 @@ bool GLSLTools::analyzeFragmentShader(const std::string& shaderCode, ShaderModel
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GLSLTools::analyzeVertexShader(const std::string& shaderCode, ShaderModel model,
|
||||
MaterialBuilder::MaterialDomain materialDomain,
|
||||
MaterialBuilder::TargetApi targetApi,
|
||||
MaterialInfo const& info) const noexcept {
|
||||
bool GLSLTools::analyzeVertexShader(const std::string& shaderCode,
|
||||
filament::backend::ShaderModel model,
|
||||
MaterialBuilder::MaterialDomain materialDomain, MaterialBuilder::TargetApi targetApi,
|
||||
MaterialBuilder::TargetLanguage targetLanguage, MaterialInfo const& info) noexcept {
|
||||
|
||||
// TODO: After implementing post-process vertex shaders, properly analyze them here.
|
||||
if (materialDomain == MaterialBuilder::MaterialDomain::POST_PROCESS) {
|
||||
@@ -159,7 +155,7 @@ bool GLSLTools::analyzeVertexShader(const std::string& shaderCode, ShaderModel m
|
||||
|
||||
GLSLangCleaner cleaner;
|
||||
int version = glslangVersionFromShaderModel(model);
|
||||
EShMessages msg = glslangFlagsFromTargetApi(targetApi);
|
||||
EShMessages msg = glslangFlagsFromTargetApi(targetApi, targetLanguage);
|
||||
bool ok = tShader.parse(&DefaultTBuiltInResource, version, false, msg);
|
||||
if (!ok) {
|
||||
utils::slog.e << "ERROR: Unable to parse vertex shader" << utils::io::endl;
|
||||
@@ -205,6 +201,7 @@ bool GLSLTools::findProperties(
|
||||
const std::string& shaderCode,
|
||||
MaterialBuilder::PropertyList& properties,
|
||||
MaterialBuilder::TargetApi targetApi,
|
||||
MaterialBuilder::TargetLanguage targetLanguage,
|
||||
ShaderModel model) const noexcept {
|
||||
const char* shaderCString = shaderCode.c_str();
|
||||
|
||||
@@ -214,7 +211,7 @@ bool GLSLTools::findProperties(
|
||||
|
||||
GLSLangCleaner cleaner;
|
||||
int version = glslangVersionFromShaderModel(model);
|
||||
EShMessages msg = glslangFlagsFromTargetApi(targetApi);
|
||||
EShMessages msg = glslangFlagsFromTargetApi(targetApi, targetLanguage);
|
||||
const TBuiltInResource* builtins = &DefaultTBuiltInResource;
|
||||
bool ok = tShader.parse(builtins, version, false, msg);
|
||||
if (!ok) {
|
||||
@@ -267,12 +264,7 @@ bool GLSLTools::findPropertyWritesOperations(const std::string& functionName, si
|
||||
}
|
||||
|
||||
std::deque<Symbol> symbols;
|
||||
bool ok = findSymbolsUsage(functionName, *rootNode, symbols);
|
||||
if (!ok) {
|
||||
utils::slog.e << "Unable to trace usage of symbols in function '" << functionName
|
||||
<< utils::io::endl;
|
||||
return false;
|
||||
}
|
||||
findSymbolsUsage(functionName, *rootNode, symbols);
|
||||
|
||||
// Iterate over symbols to see if the parameter we are interested in what written.
|
||||
std::string parameterName = functionMaterialParameters.at(parameterIdx).name;
|
||||
@@ -283,7 +275,7 @@ bool GLSLTools::findPropertyWritesOperations(const std::string& functionName, si
|
||||
}
|
||||
|
||||
// This is a direct assignment of the variable. X =
|
||||
if (symbol.getAccesses().size() == 0) {
|
||||
if (symbol.getAccesses().empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -295,7 +287,7 @@ bool GLSLTools::findPropertyWritesOperations(const std::string& functionName, si
|
||||
void GLSLTools::scanSymbolForProperty(Symbol& symbol,
|
||||
TIntermNode* rootNode,
|
||||
MaterialBuilder::PropertyList& properties) const noexcept {
|
||||
for (Access access : symbol.getAccesses()) {
|
||||
for (const Access& access : symbol.getAccesses()) {
|
||||
if (access.type == Access::Type::FunctionCall) {
|
||||
// Do NOT look into prepareMaterial call.
|
||||
if (access.string.find("prepareMaterial(struct") != std::string::npos) {
|
||||
@@ -340,7 +332,7 @@ void GLSLTools::scanSymbolForProperty(Symbol& symbol,
|
||||
}
|
||||
|
||||
bool GLSLTools::findSymbolsUsage(const std::string& functionSignature, TIntermNode& root,
|
||||
std::deque<Symbol>& symbols) const noexcept {
|
||||
std::deque<Symbol>& symbols) noexcept {
|
||||
TIntermNode* functionAST = ASTUtils::getFunctionBySignature(functionSignature, root);
|
||||
ASTUtils::traceSymbols(*functionAST, symbols);
|
||||
return true;
|
||||
@@ -359,27 +351,53 @@ int GLSLTools::glslangVersionFromShaderModel(ShaderModel model) {
|
||||
return version;
|
||||
}
|
||||
|
||||
EShMessages GLSLTools::glslangFlagsFromTargetApi(MaterialBuilder::TargetApi targetApi) {
|
||||
EShMessages msg = EShMessages::EShMsgDefault;
|
||||
if (targetApi == MaterialBuilder::TargetApi::VULKAN) {
|
||||
msg = (EShMessages) (EShMessages::EShMsgVulkanRules | EShMessages::EShMsgSpvRules);
|
||||
EShMessages GLSLTools::glslangFlagsFromTargetApi(
|
||||
MaterialBuilder::TargetApi targetApi,
|
||||
MaterialBuilder::TargetLanguage targetLanguage) {
|
||||
using TargetApi = MaterialBuilder::TargetApi;
|
||||
using TargetLanguage = MaterialBuilder::TargetLanguage;
|
||||
|
||||
switch (targetLanguage) {
|
||||
case TargetLanguage::GLSL:
|
||||
assert_invariant(targetApi == TargetApi::OPENGL);
|
||||
return EShMessages::EShMsgDefault;
|
||||
|
||||
case TargetLanguage::SPIRV:
|
||||
// issue messages for SPIR-V generation
|
||||
using Type = std::underlying_type_t<EShMessages>;
|
||||
auto msg = (Type)EShMessages::EShMsgSpvRules;
|
||||
if (targetApi == TargetApi::VULKAN) {
|
||||
// issue messages for Vulkan-requirements of GLSL for SPIR-V
|
||||
msg |= (Type)EShMessages::EShMsgVulkanRules;
|
||||
}
|
||||
if (targetApi == TargetApi::METAL) {
|
||||
// FIXME: We have to use EShMsgVulkanRules for metal, otherwise compilation will
|
||||
// choke on gl_VertexIndex.
|
||||
msg |= (Type)EShMessages::EShMsgVulkanRules;
|
||||
}
|
||||
return (EShMessages)msg;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
void GLSLTools::prepareShaderParser(MaterialBuilder::TargetApi targetApi, glslang::TShader& shader,
|
||||
EShLanguage language, int version, filamat::MaterialBuilder::Optimization optimization) {
|
||||
// We must only setup the SPIRV environment when we actually need to output SPIRV
|
||||
if (optimization == filamat::MaterialBuilder::Optimization::SIZE ||
|
||||
optimization == filamat::MaterialBuilder::Optimization::PERFORMANCE) {
|
||||
void GLSLTools::prepareShaderParser(MaterialBuilder::TargetApi targetApi,
|
||||
MaterialBuilder::TargetLanguage targetLanguage, glslang::TShader& shader,
|
||||
EShLanguage stage, int version) {
|
||||
// We must only set up the SPIRV environment when we actually need to output SPIRV
|
||||
if (targetLanguage == MaterialBuilder::TargetLanguage::SPIRV) {
|
||||
shader.setAutoMapBindings(true);
|
||||
if (targetApi == MaterialBuilder::TargetApi::VULKAN) {
|
||||
shader.setEnvInput(EShSourceGlsl, language, EShClientVulkan, version);
|
||||
shader.setEnvClient(EShClientVulkan, EShTargetVulkan_1_0);
|
||||
} else {
|
||||
assert(targetApi == MaterialBuilder::TargetApi::OPENGL);
|
||||
shader.setEnvInput(EShSourceGlsl, language, EShClientOpenGL, version);
|
||||
shader.setEnvClient(EShClientOpenGL, EShTargetOpenGL_450);
|
||||
switch (targetApi) {
|
||||
case MaterialBuilderBase::TargetApi::OPENGL:
|
||||
shader.setEnvInput(EShSourceGlsl, stage, EShClientOpenGL, version);
|
||||
shader.setEnvClient(EShClientOpenGL, EShTargetOpenGL_450);
|
||||
break;
|
||||
case MaterialBuilderBase::TargetApi::VULKAN:
|
||||
case MaterialBuilderBase::TargetApi::METAL:
|
||||
shader.setEnvInput(EShSourceGlsl, stage, EShClientVulkan, version);
|
||||
shader.setEnvClient(EShClientVulkan, EShTargetVulkan_1_0);
|
||||
break;
|
||||
case MaterialBuilderBase::TargetApi::ALL:
|
||||
// can't happen
|
||||
break;
|
||||
}
|
||||
shader.setEnvTarget(EShTargetSpv, EShTargetSpv_1_0);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,9 @@ namespace filamat {
|
||||
|
||||
// Used for symbol tracking during static code analysis.
|
||||
struct Access {
|
||||
enum Type {Swizzling, DirectIndexForStruct, FunctionCall};
|
||||
enum Type {
|
||||
Swizzling, DirectIndexForStruct, FunctionCall
|
||||
};
|
||||
Type type;
|
||||
std::string string;
|
||||
size_t parameterIdx = 0; // Only used when type == FunctionCall;
|
||||
@@ -49,8 +51,8 @@ struct Access {
|
||||
// Combinations are possible. e.g: foo(material.baseColor.xyz)
|
||||
class Symbol {
|
||||
public:
|
||||
Symbol() {}
|
||||
Symbol(const std::string& name) {
|
||||
Symbol() = default;
|
||||
explicit Symbol(const std::string& name) {
|
||||
mName = name;
|
||||
}
|
||||
|
||||
@@ -72,7 +74,7 @@ public:
|
||||
|
||||
std::string toString() const {
|
||||
std::string str(mName);
|
||||
for (Access access: mAccesses) {
|
||||
for (const Access& access: mAccesses) {
|
||||
str += ".";
|
||||
str += access.string;
|
||||
}
|
||||
@@ -80,7 +82,7 @@ public:
|
||||
}
|
||||
|
||||
bool hasDirectIndexForStruct() const noexcept {
|
||||
for (Access access : mAccesses) {
|
||||
for (const Access& access : mAccesses) {
|
||||
if (access.type == Access::Type::DirectIndexForStruct) {
|
||||
return true;
|
||||
}
|
||||
@@ -88,8 +90,8 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string getDirectIndexStructName() const noexcept {
|
||||
for (Access access : mAccesses) {
|
||||
std::string getDirectIndexStructName() const noexcept {
|
||||
for (const Access& access : mAccesses) {
|
||||
if (access.type == Access::Type::DirectIndexForStruct) {
|
||||
return access.string;
|
||||
}
|
||||
@@ -121,17 +123,15 @@ public:
|
||||
// The shader features a material() function AND
|
||||
// The shader features a prepareMaterial() function AND
|
||||
// prepareMaterial() is called at some point in material() call chain.
|
||||
bool analyzeFragmentShader(const std::string& shaderCode,
|
||||
filament::backend::ShaderModel model,
|
||||
MaterialBuilder::MaterialDomain materialDomain,
|
||||
MaterialBuilder::TargetApi targetApi, bool hasCustomSurfaceShading,
|
||||
MaterialInfo const& info) const noexcept;
|
||||
static bool analyzeFragmentShader(const std::string& shaderCode,
|
||||
filament::backend::ShaderModel model, MaterialBuilder::MaterialDomain materialDomain,
|
||||
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
|
||||
bool hasCustomSurfaceShading, MaterialInfo const& info) noexcept;
|
||||
|
||||
bool analyzeVertexShader(const std::string& shaderCode,
|
||||
static bool analyzeVertexShader(const std::string& shaderCode,
|
||||
filament::backend::ShaderModel model,
|
||||
MaterialBuilder::MaterialDomain materialDomain,
|
||||
MaterialBuilder::TargetApi targetApi,
|
||||
MaterialInfo const& info) const noexcept;
|
||||
MaterialBuilder::MaterialDomain materialDomain, MaterialBuilder::TargetApi targetApi,
|
||||
MaterialBuilder::TargetLanguage targetLanguage, MaterialInfo const& info) noexcept;
|
||||
|
||||
// Public for unit tests.
|
||||
using Property = MaterialBuilder::Property;
|
||||
@@ -143,14 +143,17 @@ public:
|
||||
const std::string& shaderCode,
|
||||
MaterialBuilder::PropertyList& properties,
|
||||
MaterialBuilder::TargetApi targetApi = MaterialBuilder::TargetApi::OPENGL,
|
||||
MaterialBuilder::TargetLanguage targetLanguage = MaterialBuilder::TargetLanguage::GLSL,
|
||||
ShaderModel model = ShaderModel::DESKTOP) const noexcept;
|
||||
|
||||
static int glslangVersionFromShaderModel(filament::backend::ShaderModel model);
|
||||
|
||||
static EShMessages glslangFlagsFromTargetApi(MaterialBuilder::TargetApi targetApi);
|
||||
static EShMessages glslangFlagsFromTargetApi(MaterialBuilder::TargetApi targetApi,
|
||||
MaterialBuilder::TargetLanguage targetLanguage);
|
||||
|
||||
static void prepareShaderParser(MaterialBuilder::TargetApi targetApi, glslang::TShader& shader,
|
||||
EShLanguage language, int version, MaterialBuilder::Optimization optimization);
|
||||
static void prepareShaderParser(MaterialBuilder::TargetApi targetApi,
|
||||
MaterialBuilder::TargetLanguage targetLanguage, glslang::TShader& shader,
|
||||
EShLanguage stage, int version);
|
||||
|
||||
static void textureLodBias(glslang::TShader& shader);
|
||||
|
||||
@@ -161,8 +164,8 @@ private:
|
||||
// in a function call.
|
||||
// Start in the function matching the signature provided and follow all out and inout calls.
|
||||
// Does NOT recurse to follow function calls.
|
||||
bool findSymbolsUsage(const std::string& functionSignature, TIntermNode& root,
|
||||
std::deque<Symbol>& symbols) const noexcept;
|
||||
static bool findSymbolsUsage(const std::string& functionSignature, TIntermNode& root,
|
||||
std::deque<Symbol>& symbols) noexcept;
|
||||
|
||||
|
||||
// Determine how a function affect one of its parameter by following all write and function call
|
||||
|
||||
@@ -40,26 +40,26 @@ io::sstream& CodeGenerator::generateSeparator(io::sstream& out) {
|
||||
}
|
||||
|
||||
utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, ShaderType type,
|
||||
MaterialInfo const& info) const {
|
||||
MaterialInfo const& material) const {
|
||||
switch (mShaderModel) {
|
||||
case ShaderModel::MOBILE:
|
||||
// Vulkan requires version 310 or higher
|
||||
if (mTargetLanguage == TargetLanguage::SPIRV ||
|
||||
info.featureLevel >= FeatureLevel::FEATURE_LEVEL_2) {
|
||||
material.featureLevel >= FeatureLevel::FEATURE_LEVEL_2) {
|
||||
// Vulkan requires layout locations on ins and outs, which were not supported
|
||||
// in the OpenGL 4.1 GLSL profile.
|
||||
out << "#version 310 es\n\n";
|
||||
} else {
|
||||
out << "#version 300 es\n\n";
|
||||
}
|
||||
if (info.hasExternalSamplers) {
|
||||
if (material.hasExternalSamplers) {
|
||||
out << "#extension GL_OES_EGL_image_external_essl3 : require\n\n";
|
||||
}
|
||||
out << "#define TARGET_MOBILE\n";
|
||||
break;
|
||||
case ShaderModel::DESKTOP:
|
||||
if (mTargetLanguage == TargetLanguage::SPIRV ||
|
||||
info.featureLevel >= FeatureLevel::FEATURE_LEVEL_2) {
|
||||
material.featureLevel >= FeatureLevel::FEATURE_LEVEL_2) {
|
||||
// Vulkan requires binding specifiers on uniforms and samplers, which were not
|
||||
// supported in the OpenGL 4.1 GLSL profile.
|
||||
out << "#version 450 core\n\n";
|
||||
@@ -95,17 +95,11 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade
|
||||
out << "#define FILAMENT_OPENGL_SEMANTICS\n";
|
||||
}
|
||||
|
||||
out << '\n';
|
||||
out << "#define FILAMENT_FEATURE_LEVEL_1 1\n";
|
||||
out << "#define FILAMENT_FEATURE_LEVEL_2 2\n";
|
||||
out << "#define FILAMENT_FEATURE_LEVEL FILAMENT_FEATURE_LEVEL_" << (int)info.featureLevel << "\n";
|
||||
|
||||
|
||||
out << '\n';
|
||||
if (mTargetApi == TargetApi::VULKAN ||
|
||||
mTargetApi == TargetApi::METAL ||
|
||||
(mTargetApi == TargetApi::OPENGL && mShaderModel == ShaderModel::DESKTOP) ||
|
||||
info.featureLevel >= FeatureLevel::FEATURE_LEVEL_2) {
|
||||
material.featureLevel >= FeatureLevel::FEATURE_LEVEL_2) {
|
||||
out << "#define FILAMENT_HAS_FEATURE_TEXTURE_GATHER\n";
|
||||
}
|
||||
|
||||
@@ -118,7 +112,13 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade
|
||||
out << "precision lowp sampler3D;\n";
|
||||
}
|
||||
|
||||
out << SHADERS_COMMON_TYPES_FS_DATA;
|
||||
// specification constants
|
||||
out << '\n';
|
||||
generateSpecificationConstant(out, "BACKEND_FEATURE_LEVEL", 0, 1);
|
||||
generateSpecificationConstant(out, "CONFIG_MAX_INSTANCES", 1, (int)CONFIG_MAX_INSTANCES);
|
||||
|
||||
out << '\n';
|
||||
out << SHADERS_COMMON_TYPES_GLSL_DATA;
|
||||
|
||||
out << "\n";
|
||||
return out;
|
||||
@@ -316,7 +316,7 @@ const char* CodeGenerator::getUniformPrecisionQualifier(UniformType type, Precis
|
||||
}
|
||||
|
||||
io::sstream& CodeGenerator::generateUniforms(io::sstream& out, ShaderType type,
|
||||
BindingPoints binding, const UniformInterfaceBlock& uib) const {
|
||||
UniformBindingPoints binding, const UniformInterfaceBlock& uib) const {
|
||||
auto const& infos = uib.getUniformInfoList();
|
||||
if (infos.empty()) {
|
||||
return out;
|
||||
@@ -329,11 +329,23 @@ io::sstream& CodeGenerator::generateUniforms(io::sstream& out, ShaderType type,
|
||||
Precision uniformPrecision = getDefaultUniformPrecision();
|
||||
Precision defaultPrecision = getDefaultPrecision(type);
|
||||
|
||||
// This constant must match the equivalent in MetalState.h.
|
||||
static constexpr uint32_t METAL_UNIFORM_BUFFER_BINDING_START = 17u;
|
||||
|
||||
out << "\nlayout(";
|
||||
// TODO: at feature level 2, GLSL should support the binding qualifier
|
||||
if (mTargetLanguage == TargetLanguage::SPIRV) {
|
||||
uint32_t bindingIndex = (uint32_t) binding; // avoid char output
|
||||
out << "binding = " << bindingIndex << ", ";
|
||||
const auto bindingIndex = (uint32_t) binding; // avoid char output
|
||||
switch (mTargetApi) {
|
||||
case TargetApi::METAL:
|
||||
out << "binding = " << METAL_UNIFORM_BUFFER_BINDING_START + bindingIndex << ", ";
|
||||
break;
|
||||
|
||||
default:
|
||||
case TargetApi::VULKAN:
|
||||
out << "binding = " << bindingIndex << ", ";
|
||||
break;
|
||||
}
|
||||
}
|
||||
out << "std140) uniform " << blockName << " {\n";
|
||||
for (auto const& info : infos) {
|
||||
@@ -344,7 +356,11 @@ io::sstream& CodeGenerator::generateUniforms(io::sstream& out, ShaderType type,
|
||||
if (precision[0] != '\0') out << " ";
|
||||
out << type << " " << info.name.c_str();
|
||||
if (info.size > 0) {
|
||||
out << "[" << info.size << "]";
|
||||
if (info.sizeName.empty()) {
|
||||
out << "[" << info.size << "]";
|
||||
} else {
|
||||
out << "[" << info.sizeName.c_str() << "]";
|
||||
}
|
||||
}
|
||||
out << ";\n";
|
||||
}
|
||||
@@ -361,11 +377,6 @@ io::sstream& CodeGenerator::generateSamplers(
|
||||
}
|
||||
|
||||
for (auto const& info : infos) {
|
||||
|
||||
CString uniformName =
|
||||
SamplerInterfaceBlock::getUniformName(
|
||||
sib.getName().c_str(), info.name.c_str());
|
||||
|
||||
auto type = info.type;
|
||||
if (type == SamplerType::SAMPLER_EXTERNAL && mShaderModel != ShaderModel::MOBILE) {
|
||||
// we're generating the shader for the desktop, where we assume external textures
|
||||
@@ -392,7 +403,7 @@ io::sstream& CodeGenerator::generateSamplers(
|
||||
|
||||
out << ") ";
|
||||
}
|
||||
out << "uniform " << precision << " " << typeName << " " << uniformName.c_str();
|
||||
out << "uniform " << precision << " " << typeName << " " << info.uniformName.c_str();
|
||||
out << ";\n";
|
||||
}
|
||||
out << "\n";
|
||||
@@ -406,7 +417,7 @@ io::sstream& CodeGenerator::generateSubpass(io::sstream& out, SubpassInfo subpas
|
||||
}
|
||||
|
||||
CString subpassName =
|
||||
SamplerInterfaceBlock::getUniformName(subpass.block.c_str(), subpass.name.c_str());
|
||||
SamplerInterfaceBlock::generateUniformName(subpass.block.c_str(), subpass.name.c_str());
|
||||
|
||||
char const* const typeName = "subpassInput";
|
||||
// In our Vulkan backend, subpass inputs always live in descriptor set 2. (ignored for GLES)
|
||||
@@ -435,18 +446,13 @@ void CodeGenerator::fixupExternalSamplers(
|
||||
// been swapped during a previous optimization step
|
||||
for (auto const& info : infos) {
|
||||
if (info.type == SamplerType::SAMPLER_EXTERNAL) {
|
||||
|
||||
CString uniformName =
|
||||
SamplerInterfaceBlock::getUniformName(
|
||||
sib.getName().c_str(), info.name.c_str());
|
||||
|
||||
auto name = std::string("sampler2D ") + uniformName.c_str();
|
||||
auto name = std::string("sampler2D ") + info.uniformName.c_str();
|
||||
size_t index = shader.find(name);
|
||||
|
||||
if (index != std::string::npos) {
|
||||
hasExternalSampler = true;
|
||||
auto newName =
|
||||
std::string("samplerExternalOES ") + uniformName.c_str();
|
||||
std::string("samplerExternalOES ") + info.uniformName.c_str();
|
||||
shader.replace(index, name.size(), newName);
|
||||
}
|
||||
}
|
||||
@@ -491,6 +497,27 @@ io::sstream& CodeGenerator::generateIndexedDefine(io::sstream& out, const char*
|
||||
return out;
|
||||
}
|
||||
|
||||
utils::io::sstream& CodeGenerator::generateSpecificationConstant(utils::io::sstream& out,
|
||||
const char* name, uint32_t id, std::variant<int, float, bool> value) const {
|
||||
static const char* types[] = { "int", "float", "bool" };
|
||||
if (mTargetLanguage == MaterialBuilderBase::TargetLanguage::SPIRV) {
|
||||
std::visit([&](auto&& arg) {
|
||||
out << "layout (constant_id = " << id << ") const "
|
||||
<< types[value.index()] << " " << name << " = " << arg << ";\n";
|
||||
}, value);
|
||||
} else {
|
||||
std::visit([&](auto&& arg) {
|
||||
out << "#ifndef SPIRV_CROSS_CONSTANT_ID_" << id << '\n'
|
||||
<< "#define SPIRV_CROSS_CONSTANT_ID_" << id << " " << arg << '\n'
|
||||
<< "#endif" << '\n'
|
||||
<< "const " << types[value.index()] << " " << name << " = SPIRV_CROSS_CONSTANT_ID_" << id
|
||||
<< ";\n";
|
||||
}, value);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
io::sstream& CodeGenerator::generateMaterialProperty(io::sstream& out,
|
||||
MaterialBuilder::Property property, bool isSet) {
|
||||
if (isSet) {
|
||||
|
||||
@@ -19,19 +19,21 @@
|
||||
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <filamat/MaterialBuilder.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <private/filament/EngineEnums.h>
|
||||
#include <filament/MaterialEnums.h>
|
||||
|
||||
#include <private/filament/EngineEnums.h>
|
||||
#include <private/filament/SamplerInterfaceBlock.h>
|
||||
#include <private/filament/UniformInterfaceBlock.h>
|
||||
#include <private/filament/SubpassInfo.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/sstream.h>
|
||||
|
||||
#include <private/filament/Variant.h>
|
||||
@@ -61,7 +63,7 @@ public:
|
||||
|
||||
// generate prolog for the given shader
|
||||
utils::io::sstream& generateProlog(utils::io::sstream& out, ShaderType type,
|
||||
MaterialInfo const& info) const;
|
||||
MaterialInfo const& material) const;
|
||||
|
||||
static utils::io::sstream& generateEpilog(utils::io::sstream& out);
|
||||
|
||||
@@ -108,7 +110,7 @@ public:
|
||||
|
||||
// generate uniforms
|
||||
utils::io::sstream& generateUniforms(utils::io::sstream& out, ShaderType type,
|
||||
filament::BindingPoints binding, const filament::UniformInterfaceBlock& uib) const;
|
||||
filament::UniformBindingPoints binding, const filament::UniformInterfaceBlock& uib) const;
|
||||
|
||||
// generate samplers
|
||||
utils::io::sstream& generateSamplers(utils::io::sstream& out, uint8_t firstBinding,
|
||||
@@ -130,6 +132,9 @@ public:
|
||||
static utils::io::sstream& generateIndexedDefine(utils::io::sstream& out, const char* name,
|
||||
uint32_t index, uint32_t value);
|
||||
|
||||
utils::io::sstream& generateSpecificationConstant(utils::io::sstream& out,
|
||||
const char* name, uint32_t id, std::variant<int, float, bool> value) const;
|
||||
|
||||
static utils::io::sstream& generatePostProcessGetters(utils::io::sstream& out, ShaderType type);
|
||||
static utils::io::sstream& generateGetters(utils::io::sstream& out, ShaderType type);
|
||||
static utils::io::sstream& generateParameters(utils::io::sstream& out, ShaderType type);
|
||||
|
||||
@@ -18,9 +18,12 @@
|
||||
#define TNT_FILAMAT_MATERIALINFO_H
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include "../SamplerBindingMap.h"
|
||||
|
||||
#include <filament/MaterialEnums.h>
|
||||
|
||||
#include <private/filament/UniformInterfaceBlock.h>
|
||||
#include <private/filament/SamplerBindingMap.h>
|
||||
#include <private/filament/SamplerInterfaceBlock.h>
|
||||
#include <private/filament/SubpassInfo.h>
|
||||
|
||||
|
||||
@@ -19,13 +19,13 @@
|
||||
#include <filament/MaterialEnums.h>
|
||||
|
||||
#include <private/filament/EngineEnums.h>
|
||||
#include <private/filament/SibGenerator.h>
|
||||
#include <private/filament/Variant.h>
|
||||
|
||||
#include <utils/CString.h>
|
||||
|
||||
#include "filamat/MaterialBuilder.h"
|
||||
#include "CodeGenerator.h"
|
||||
#include "../SibGenerator.h"
|
||||
#include "../UibGenerator.h"
|
||||
|
||||
#include <iterator>
|
||||
@@ -266,26 +266,26 @@ std::string ShaderGenerator::createVertexProgram(ShaderModel shaderModel,
|
||||
|
||||
// uniforms
|
||||
cg.generateUniforms(vs, ShaderType::VERTEX,
|
||||
BindingPoints::PER_VIEW, UibGenerator::getPerViewUib());
|
||||
UniformBindingPoints::PER_VIEW, UibGenerator::getPerViewUib());
|
||||
cg.generateUniforms(vs, ShaderType::VERTEX,
|
||||
BindingPoints::PER_RENDERABLE, UibGenerator::getPerRenderableUib());
|
||||
UniformBindingPoints::PER_RENDERABLE, UibGenerator::getPerRenderableUib());
|
||||
if (variant.hasSkinningOrMorphing()) {
|
||||
cg.generateUniforms(vs, ShaderType::VERTEX,
|
||||
BindingPoints::PER_RENDERABLE_BONES,
|
||||
UniformBindingPoints::PER_RENDERABLE_BONES,
|
||||
UibGenerator::getPerRenderableBonesUib());
|
||||
cg.generateUniforms(vs, ShaderType::VERTEX,
|
||||
BindingPoints::PER_RENDERABLE_MORPHING,
|
||||
UniformBindingPoints::PER_RENDERABLE_MORPHING,
|
||||
UibGenerator::getPerRenderableMorphingUib());
|
||||
cg.generateSamplers(vs,
|
||||
material.samplerBindings.getBlockOffset(BindingPoints::PER_RENDERABLE_MORPHING),
|
||||
material.samplerBindings.getBlockOffset(SamplerBindingPoints::PER_RENDERABLE_MORPHING),
|
||||
SibGenerator::getPerRenderPrimitiveMorphingSib(variant));
|
||||
}
|
||||
cg.generateUniforms(vs, ShaderType::VERTEX,
|
||||
BindingPoints::PER_MATERIAL_INSTANCE, material.uib);
|
||||
UniformBindingPoints::PER_MATERIAL_INSTANCE, material.uib);
|
||||
CodeGenerator::generateSeparator(vs);
|
||||
// TODO: should we generate per-view SIB in the vertex shader?
|
||||
cg.generateSamplers(vs,
|
||||
material.samplerBindings.getBlockOffset(BindingPoints::PER_MATERIAL_INSTANCE),
|
||||
material.samplerBindings.getBlockOffset(SamplerBindingPoints::PER_MATERIAL_INSTANCE),
|
||||
material.sib);
|
||||
|
||||
// shader code
|
||||
@@ -461,25 +461,25 @@ std::string ShaderGenerator::createFragmentProgram(ShaderModel shaderModel,
|
||||
|
||||
// uniforms and samplers
|
||||
cg.generateUniforms(fs, ShaderType::FRAGMENT,
|
||||
BindingPoints::PER_VIEW, UibGenerator::getPerViewUib());
|
||||
UniformBindingPoints::PER_VIEW, UibGenerator::getPerViewUib());
|
||||
cg.generateUniforms(fs, ShaderType::FRAGMENT,
|
||||
BindingPoints::PER_RENDERABLE, UibGenerator::getPerRenderableUib());
|
||||
UniformBindingPoints::PER_RENDERABLE, UibGenerator::getPerRenderableUib());
|
||||
cg.generateUniforms(fs, ShaderType::FRAGMENT,
|
||||
BindingPoints::LIGHTS, UibGenerator::getLightsUib());
|
||||
UniformBindingPoints::LIGHTS, UibGenerator::getLightsUib());
|
||||
if (litVariants && filament::Variant::isShadowReceiverVariant(variant)) {
|
||||
cg.generateUniforms(fs, ShaderType::FRAGMENT,
|
||||
BindingPoints::SHADOW, UibGenerator::getShadowUib());
|
||||
UniformBindingPoints::SHADOW, UibGenerator::getShadowUib());
|
||||
}
|
||||
cg.generateUniforms(fs, ShaderType::FRAGMENT,
|
||||
BindingPoints::FROXEL_RECORDS, UibGenerator::getFroxelRecordUib());
|
||||
UniformBindingPoints::FROXEL_RECORDS, UibGenerator::getFroxelRecordUib());
|
||||
cg.generateUniforms(fs, ShaderType::FRAGMENT,
|
||||
BindingPoints::PER_MATERIAL_INSTANCE, material.uib);
|
||||
UniformBindingPoints::PER_MATERIAL_INSTANCE, material.uib);
|
||||
CodeGenerator::generateSeparator(fs);
|
||||
cg.generateSamplers(fs,
|
||||
material.samplerBindings.getBlockOffset(BindingPoints::PER_VIEW),
|
||||
material.samplerBindings.getBlockOffset(SamplerBindingPoints::PER_VIEW),
|
||||
SibGenerator::getPerViewSib(variant));
|
||||
cg.generateSamplers(fs,
|
||||
material.samplerBindings.getBlockOffset(BindingPoints::PER_MATERIAL_INSTANCE),
|
||||
material.samplerBindings.getBlockOffset(SamplerBindingPoints::PER_MATERIAL_INSTANCE),
|
||||
material.sib);
|
||||
|
||||
fs << "float filament_lodBias;\n";
|
||||
@@ -551,12 +551,12 @@ std::string ShaderGenerator::createPostProcessVertexProgram(ShaderModel sm,
|
||||
generatePostProcessMaterialVariantDefines(vs, PostProcessVariant(variantKey));
|
||||
|
||||
cg.generateUniforms(vs, ShaderType::VERTEX,
|
||||
BindingPoints::PER_VIEW, UibGenerator::getPerViewUib());
|
||||
UniformBindingPoints::PER_VIEW, UibGenerator::getPerViewUib());
|
||||
cg.generateUniforms(vs, ShaderType::VERTEX,
|
||||
BindingPoints::PER_MATERIAL_INSTANCE, material.uib);
|
||||
UniformBindingPoints::PER_MATERIAL_INSTANCE, material.uib);
|
||||
|
||||
cg.generateSamplers(vs,
|
||||
material.samplerBindings.getBlockOffset(BindingPoints::PER_MATERIAL_INSTANCE),
|
||||
material.samplerBindings.getBlockOffset(SamplerBindingPoints::PER_MATERIAL_INSTANCE),
|
||||
material.sib);
|
||||
|
||||
CodeGenerator::generatePostProcessCommon(vs, ShaderType::VERTEX);
|
||||
@@ -588,12 +588,12 @@ std::string ShaderGenerator::createPostProcessFragmentProgram(ShaderModel sm,
|
||||
}
|
||||
|
||||
cg.generateUniforms(fs, ShaderType::FRAGMENT,
|
||||
BindingPoints::PER_VIEW, UibGenerator::getPerViewUib());
|
||||
UniformBindingPoints::PER_VIEW, UibGenerator::getPerViewUib());
|
||||
cg.generateUniforms(fs, ShaderType::FRAGMENT,
|
||||
BindingPoints::PER_MATERIAL_INSTANCE, material.uib);
|
||||
UniformBindingPoints::PER_MATERIAL_INSTANCE, material.uib);
|
||||
|
||||
cg.generateSamplers(fs,
|
||||
material.samplerBindings.getBlockOffset(BindingPoints::PER_MATERIAL_INSTANCE),
|
||||
material.samplerBindings.getBlockOffset(SamplerBindingPoints::PER_MATERIAL_INSTANCE),
|
||||
material.sib);
|
||||
|
||||
// subpass
|
||||
|
||||
@@ -45,13 +45,13 @@ static ::testing::AssertionResult PropertyListsMatch(const MaterialBuilder::Prop
|
||||
}
|
||||
|
||||
std::string shaderWithAllProperties(JobSystem& jobSystem, ShaderType type,
|
||||
const std::string fragmentCode, const std::string vertexCode = "",
|
||||
const std::string& fragmentCode, const std::string& vertexCode = "",
|
||||
filamat::MaterialBuilder::Shading shadingModel = filamat::MaterialBuilder::Shading::LIT,
|
||||
filamat::MaterialBuilder::RefractionMode refractionMode = filamat::MaterialBuilder::RefractionMode::NONE,
|
||||
filamat::MaterialBuilder::VertexDomain vertexDomain = filamat::MaterialBuilder::VertexDomain::OBJECT) {
|
||||
MockIncluder includer;
|
||||
includer
|
||||
.sourceForInclude("modify_normal.h", "material.normal = vec3(0.8);");
|
||||
.sourceForInclude("modify_normal.h", "material.normal = vec3(0.8);");
|
||||
|
||||
filamat::MaterialBuilder builder;
|
||||
builder.material(fragmentCode.c_str());
|
||||
@@ -69,8 +69,11 @@ std::string shaderWithAllProperties(JobSystem& jobSystem, ShaderType type,
|
||||
// We need to "build" the material to resolve any includes in user code.
|
||||
builder.build(jobSystem);
|
||||
|
||||
return builder.peek(type,
|
||||
{1, MaterialBuilder::TargetApi::OPENGL, MaterialBuilder::TargetLanguage::GLSL},
|
||||
return builder.peek(type, {
|
||||
ShaderModel::MOBILE,
|
||||
MaterialBuilder::TargetApi::OPENGL,
|
||||
MaterialBuilder::TargetLanguage::GLSL
|
||||
},
|
||||
allProperties);
|
||||
}
|
||||
|
||||
@@ -86,19 +89,17 @@ TEST(StaticCodeAnalysisHelper, getFunctionNameNoParenthesis) {
|
||||
|
||||
class MaterialCompiler : public ::testing::Test {
|
||||
protected:
|
||||
MaterialCompiler() {
|
||||
}
|
||||
MaterialCompiler() = default;
|
||||
|
||||
virtual ~MaterialCompiler() {
|
||||
}
|
||||
~MaterialCompiler() override = default;
|
||||
|
||||
virtual void SetUp() {
|
||||
void SetUp() override {
|
||||
jobSystem = std::make_unique<JobSystem>();
|
||||
jobSystem->adopt();
|
||||
MaterialBuilder::init();
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
void TearDown() override {
|
||||
jobSystem->emancipate();
|
||||
MaterialBuilder::shutdown();
|
||||
}
|
||||
@@ -113,12 +114,13 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerNothingDetected) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode);
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT,
|
||||
fragmentCode);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
|
||||
@@ -137,9 +139,9 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerNothingDetectedVertex) {
|
||||
fragmentCode, vertexCode);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::VERTEX, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
|
||||
@@ -155,12 +157,13 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerNotFollowingINParameters) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode);
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT,
|
||||
fragmentCode);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
|
||||
@@ -172,12 +175,13 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerDirectAssign) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode);
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT,
|
||||
fragmentCode);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::BASE_COLOR)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
@@ -201,9 +205,9 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerDirectAssignVertex) {
|
||||
MaterialBuilder::VertexDomain::DEVICE);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::VERTEX, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::CLIP_SPACE_TRANSFORM)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
@@ -216,12 +220,13 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerDirectAssignWithSwizzling) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode);
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT,
|
||||
fragmentCode);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::BASE_COLOR)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
@@ -239,12 +244,13 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerSymbolAsOutParameterWithAliasing) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode);
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT,
|
||||
fragmentCode);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::BASE_COLOR)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
@@ -262,12 +268,13 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerSymbolAsOutParameterWithAliasingAndSw
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode);
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT,
|
||||
fragmentCode);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::BASE_COLOR)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
@@ -290,12 +297,13 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerSymbolInOutInChainWithDirectIndexInto
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode);
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT,
|
||||
fragmentCode);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::BASE_COLOR)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
@@ -318,12 +326,13 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerSymbolInOutInChain) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode);
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT,
|
||||
fragmentCode);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::BASE_COLOR)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
@@ -337,12 +346,13 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerBaseColor) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode);
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT,
|
||||
fragmentCode);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::BASE_COLOR)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
@@ -355,12 +365,13 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerRoughness) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode);
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT,
|
||||
fragmentCode);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::ROUGHNESS)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
@@ -373,12 +384,13 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerMetallic) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode);
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT,
|
||||
fragmentCode);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::METALLIC)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
@@ -391,12 +403,13 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerReflectance) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode);
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT,
|
||||
fragmentCode);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::REFLECTANCE)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
@@ -409,12 +422,13 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerAmbientOcclusion) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode);
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT,
|
||||
fragmentCode);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::AMBIENT_OCCLUSION)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
@@ -427,12 +441,13 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerClearCoat) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode);
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT,
|
||||
fragmentCode);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::CLEAR_COAT)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
@@ -447,14 +462,15 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerTransmission) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode, "",
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode,
|
||||
"",
|
||||
filamat::MaterialBuilder::Shading::LIT,
|
||||
filamat::MaterialBuilder::RefractionMode::CUBEMAP);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::ABSORPTION)] = true;
|
||||
expected[size_t(filamat::MaterialBuilder::Property::TRANSMISSION)] = true;
|
||||
expected[size_t(filamat::MaterialBuilder::Property::IOR)] = true;
|
||||
@@ -469,12 +485,13 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerClearCoatRoughness) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode, "");
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode,
|
||||
"");
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::CLEAR_COAT_ROUGHNESS)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
@@ -488,12 +505,13 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerClearCoatNormal) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode, "");
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode,
|
||||
"");
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::CLEAR_COAT)] = true;
|
||||
expected[size_t(filamat::MaterialBuilder::Property::CLEAR_COAT_NORMAL)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
@@ -507,13 +525,14 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerThickness) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode, "",
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode,
|
||||
"",
|
||||
filamat::MaterialBuilder::Shading::SUBSURFACE);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::THICKNESS)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
@@ -526,13 +545,14 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerSubsurfacePower) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode, "",
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode,
|
||||
"",
|
||||
filamat::MaterialBuilder::Shading::SUBSURFACE);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::SUBSURFACE_POWER)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
@@ -545,13 +565,14 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerSubsurfaceColor) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode, "",
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode,
|
||||
"",
|
||||
filamat::MaterialBuilder::Shading::SUBSURFACE);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::SUBSURFACE_COLOR)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
@@ -564,12 +585,13 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerAnisotropicDirection) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode);
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT,
|
||||
fragmentCode);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::ANISOTROPY_DIRECTION)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
@@ -582,12 +604,13 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerAnisotropic) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode);
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT,
|
||||
fragmentCode);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::ANISOTROPY)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
@@ -600,13 +623,14 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerSheenColor) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode, "",
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode,
|
||||
"",
|
||||
filamat::MaterialBuilder::Shading::CLOTH);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::SHEEN_COLOR)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
@@ -619,12 +643,13 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerSheenRoughness) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode);
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT,
|
||||
fragmentCode);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::SHEEN_ROUGHNESS)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
@@ -637,12 +662,13 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerNormal) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode);
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT,
|
||||
fragmentCode);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::NORMAL)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
@@ -655,12 +681,13 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerBentNormal) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode);
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT,
|
||||
fragmentCode);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::BENT_NORMAL)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
@@ -673,12 +700,13 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerOutputFactor) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode);
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT,
|
||||
fragmentCode);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::POST_LIGHTING_COLOR)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
@@ -691,12 +719,13 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerWithinInclude) {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT, fragmentCode);
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderType::FRAGMENT,
|
||||
fragmentCode);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties {false};
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(filament::backend::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected {false};
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::NORMAL)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
|
||||
@@ -131,8 +131,8 @@ private:
|
||||
mutable std::unordered_map<uint64_t, filament::Material*> mGltfMaterialCache;
|
||||
filament::Texture* mDefaultMap = nullptr;
|
||||
filament::Texture* mDefaultNormalMap = nullptr;
|
||||
float mDefaultMetallic = 0.0;
|
||||
float mDefaultRoughness = 0.4;
|
||||
float mDefaultMetallic = 0.0f;
|
||||
float mDefaultRoughness = 0.4f;
|
||||
filament::sRGBColor mDefaultEmissive = filament::sRGBColor({0.0f, 0.0f, 0.0f});
|
||||
|
||||
std::vector<utils::Entity> mRenderables;
|
||||
|
||||
@@ -157,9 +157,10 @@ public:
|
||||
* Consumes the contents of a glTF 2.0 file and produces a primary asset with one or more
|
||||
* instances. The primary asset has ownership over the instances.
|
||||
*
|
||||
* The returned instances share their textures, material instances, and vertex buffers with the
|
||||
* primary asset. However each instance has its own unique set of entities, transform
|
||||
* components, and renderable components. Instances are freed when the primary asset is freed.
|
||||
* The returned instances share their textures, materials, and vertex buffers with the primary
|
||||
* asset. However each instance has its own unique set of entities, transform components,
|
||||
* material instances, and renderable components. Instances are freed when the primary asset is
|
||||
* freed.
|
||||
*
|
||||
* Light components are not instanced, they belong only to the primary asset.
|
||||
*
|
||||
@@ -201,7 +202,8 @@ public:
|
||||
void enableDiagnostics(bool enable = true);
|
||||
|
||||
/**
|
||||
* Destroys the given asset and all of its associated Filament objects.
|
||||
* Destroys the given asset, all of its associated Filament objects, and all associated
|
||||
* FilamentInstance objects.
|
||||
*
|
||||
* This destroys entities, components, material instances, vertex buffers, index buffers,
|
||||
* and textures. This does not necessarily immediately free all source data, since
|
||||
|
||||
@@ -318,6 +318,11 @@ public:
|
||||
*/
|
||||
void detachMaterialInstances();
|
||||
|
||||
/**
|
||||
* Convenience function to get the first instance (which always exists).
|
||||
*/
|
||||
FilamentInstance* getInstance() noexcept { return getAssetInstances()[0]; }
|
||||
|
||||
/*! \cond PRIVATE */
|
||||
|
||||
FilamentInstance** getAssetInstances() noexcept;
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Entity.h>
|
||||
|
||||
#include <filament/Box.h>
|
||||
|
||||
namespace filament::gltfio {
|
||||
|
||||
class Animator;
|
||||
@@ -109,6 +111,24 @@ public:
|
||||
* This is a no-op if the given skin index or target is invalid.
|
||||
*/
|
||||
void detachSkin(size_t skinIndex, utils::Entity target) noexcept;
|
||||
|
||||
/**
|
||||
* Resets the AABB on all renderables by manually computing the bounding box.
|
||||
*
|
||||
* THIS IS ONLY USEFUL FOR MALFORMED ASSETS THAT DO NOT HAVE MIN/MAX SET UP CORRECTLY.
|
||||
*
|
||||
* Does not affect the return value of getBoundingBox() on the owning asset.
|
||||
* Cannot be called after releaseSourceData() on the owning asset.
|
||||
* Can only be called after loadResources() or asyncBeginLoad().
|
||||
*/
|
||||
void recomputeBoundingBoxes();
|
||||
|
||||
/**
|
||||
* Gets the axis-aligned bounding box from the supplied min / max values in glTF accessors.
|
||||
*
|
||||
* If recomputeBoundingBoxes() has been called, then this returns the recomputed AABB.
|
||||
*/
|
||||
Aabb getBoundingBox() const noexcept;
|
||||
};
|
||||
|
||||
} // namespace filament::gltfio
|
||||
|
||||
@@ -36,8 +36,8 @@ enum class AlphaMode : uint8_t {
|
||||
|
||||
// The following struct gets hashed so all padding bits should be explicit.
|
||||
// Tell the compiler to emit a warning if it adds any padding.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic warning "-Wpadded"
|
||||
UTILS_WARNING_PUSH
|
||||
UTILS_WARNING_ENABLE_PADDED
|
||||
|
||||
/**
|
||||
* \struct MaterialKey MaterialProvider.h gltfio/MaterialProvider.h
|
||||
@@ -98,7 +98,7 @@ struct alignas(4) MaterialKey {
|
||||
|
||||
static_assert(sizeof(MaterialKey) == 16, "MaterialKey has unexpected size.");
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
UTILS_WARNING_POP
|
||||
|
||||
bool operator==(const MaterialKey& k1, const MaterialKey& k2);
|
||||
|
||||
|
||||
@@ -49,14 +49,6 @@ struct ResourceConfiguration {
|
||||
//! If true, adjusts skinning weights to sum to 1. Well formed glTF files do not need this,
|
||||
//! but it is useful for robustness.
|
||||
bool normalizeSkinningWeights;
|
||||
|
||||
//! If true, computes the bounding boxes of all \c POSITION attibutes. Well formed glTF files
|
||||
//! do not need this, but it is useful for robustness.
|
||||
bool recomputeBoundingBoxes;
|
||||
|
||||
//! If true, ignores skinning when computing bounding boxes. Implicitly true for instanced
|
||||
//! assets. Only applicable when recomputeBoundingBoxes is set to true.
|
||||
bool ignoreBindTransform;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -162,7 +154,6 @@ public:
|
||||
private:
|
||||
bool loadResources(FFilamentAsset* asset, bool async);
|
||||
void normalizeSkinningWeights(FFilamentAsset* asset) const;
|
||||
void updateBoundingBoxes(FFilamentAsset* asset) const;
|
||||
AssetPool* mPool;
|
||||
struct Impl;
|
||||
Impl* pImpl;
|
||||
|
||||
@@ -81,7 +81,7 @@ UTILS_PUBLIC inline void decomposeMatrix(const filament::math::mat4f& mat, filam
|
||||
*rotation = clone.toQuaternion();
|
||||
} else {
|
||||
// Set to identity if close to zero
|
||||
*rotation = quatf(1);
|
||||
*rotation = quatf(1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,10 +76,13 @@ struct AnimatorImpl {
|
||||
TransformManager* transformManager;
|
||||
vector<float> weights;
|
||||
FixedCapacityVector<mat4f> crossFade;
|
||||
void addChannels(const NodeMap& nodeMap, const cgltf_animation& srcAnim, Animation& dst);
|
||||
void addChannels(const FixedCapacityVector<Entity>& nodeMap, const cgltf_animation& srcAnim,
|
||||
Animation& dst);
|
||||
void applyAnimation(const Channel& channel, float t, size_t prevIndex, size_t nextIndex);
|
||||
void stashCrossFade();
|
||||
void applyCrossFade(float alpha);
|
||||
void resetBoneMatrices(FFilamentInstance* instance);
|
||||
void updateBoneMatrices(FFilamentInstance* instance);
|
||||
};
|
||||
|
||||
static void createSampler(const cgltf_animation_sampler& src, Sampler& dst) {
|
||||
@@ -298,71 +301,28 @@ void Animator::applyAnimation(size_t animationIndex, float time) const {
|
||||
}
|
||||
|
||||
void Animator::resetBoneMatrices() {
|
||||
auto renderableManager = mImpl->renderableManager;
|
||||
|
||||
auto update = [=](const SkinVector& skins, BoneVector& boneVector) {
|
||||
for (const auto& skin : skins) {
|
||||
size_t njoints = skin.joints.size();
|
||||
boneVector.resize(njoints);
|
||||
for (const auto& entity : skin.targets) {
|
||||
auto renderable = renderableManager->getInstance(entity);
|
||||
if (renderable) {
|
||||
for (size_t boneIndex = 0; boneIndex < njoints; ++boneIndex) {
|
||||
boneVector[boneIndex] = mat4f();
|
||||
}
|
||||
renderableManager->setBones(renderable, boneVector.data(), boneVector.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// If this is a single-instance animator, then reset only this instance.
|
||||
if (mImpl->instance) {
|
||||
update(mImpl->instance->skins, mImpl->boneMatrices);
|
||||
} else {
|
||||
for (FFilamentInstance* instance : mImpl->asset->mInstances) {
|
||||
update(instance->skins, mImpl->boneMatrices);
|
||||
}
|
||||
mImpl->resetBoneMatrices(mImpl->instance);
|
||||
return;
|
||||
}
|
||||
|
||||
// If this is a broadcast animator, then reset all instances.
|
||||
for (FFilamentInstance* instance : mImpl->asset->mInstances) {
|
||||
mImpl->resetBoneMatrices(instance);
|
||||
}
|
||||
}
|
||||
|
||||
void Animator::updateBoneMatrices() {
|
||||
auto renderableManager = mImpl->renderableManager;
|
||||
auto transformManager = mImpl->transformManager;
|
||||
|
||||
auto update = [=](const SkinVector& skins, BoneVector& boneVector) {
|
||||
for (const Skin& skin : skins) {
|
||||
size_t njoints = skin.joints.size();
|
||||
boneVector.resize(njoints);
|
||||
for (Entity entity : skin.targets) {
|
||||
auto renderable = renderableManager->getInstance(entity);
|
||||
if (!renderable) {
|
||||
continue;
|
||||
}
|
||||
mat4f inverseGlobalTransform;
|
||||
auto xformable = transformManager->getInstance(entity);
|
||||
if (xformable) {
|
||||
inverseGlobalTransform = inverse(transformManager->getWorldTransform(xformable));
|
||||
}
|
||||
for (size_t boneIndex = 0; boneIndex < njoints; ++boneIndex) {
|
||||
const auto& joint = skin.joints[boneIndex];
|
||||
TransformManager::Instance jointInstance = transformManager->getInstance(joint);
|
||||
mat4f globalJointTransform = transformManager->getWorldTransform(jointInstance);
|
||||
boneVector[boneIndex] =
|
||||
inverseGlobalTransform *
|
||||
globalJointTransform *
|
||||
skin.inverseBindMatrices[boneIndex];
|
||||
}
|
||||
renderableManager->setBones(renderable, boneVector.data(), boneVector.size());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// If this is a single-instance animator, then update only this instance.
|
||||
if (mImpl->instance) {
|
||||
update(mImpl->instance->skins, mImpl->boneMatrices);
|
||||
} else {
|
||||
for (FFilamentInstance* instance : mImpl->asset->mInstances) {
|
||||
update(instance->skins, mImpl->boneMatrices);
|
||||
}
|
||||
mImpl->updateBoneMatrices(mImpl->instance);
|
||||
return;
|
||||
}
|
||||
|
||||
// If this is a broadcast animator, then update all instances.
|
||||
for (FFilamentInstance* instance : mImpl->asset->mInstances) {
|
||||
mImpl->updateBoneMatrices(instance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,15 +387,16 @@ void AnimatorImpl::applyCrossFade(float alpha) {
|
||||
recursiveFn(tm.getInstance(asset->mRoot), 0, recursiveFn);
|
||||
}
|
||||
|
||||
void AnimatorImpl::addChannels(const NodeMap& nodeMap, const cgltf_animation& srcAnim,
|
||||
Animation& dst) {
|
||||
cgltf_animation_channel* srcChannels = srcAnim.channels;
|
||||
cgltf_animation_sampler* srcSamplers = srcAnim.samplers;
|
||||
void AnimatorImpl::addChannels(const FixedCapacityVector<Entity>& nodeMap,
|
||||
const cgltf_animation& srcAnim, Animation& dst) {
|
||||
const cgltf_animation_channel* srcChannels = srcAnim.channels;
|
||||
const cgltf_animation_sampler* srcSamplers = srcAnim.samplers;
|
||||
const cgltf_node* nodes = asset->mSourceAsset->hierarchy->nodes;
|
||||
const Sampler* samplers = dst.samplers.data();
|
||||
for (cgltf_size j = 0, nchans = srcAnim.channels_count; j < nchans; ++j) {
|
||||
const cgltf_animation_channel& srcChannel = srcChannels[j];
|
||||
auto iter = nodeMap.find(srcChannel.target_node);
|
||||
if (UTILS_UNLIKELY(iter == nodeMap.end())) {
|
||||
Entity targetEntity = nodeMap[srcChannel.target_node - nodes];
|
||||
if (UTILS_UNLIKELY(!targetEntity)) {
|
||||
if (GLTFIO_VERBOSE) {
|
||||
slog.w << "No scene root contains node ";
|
||||
if (srcChannel.target_node->name) {
|
||||
@@ -449,7 +410,6 @@ void AnimatorImpl::addChannels(const NodeMap& nodeMap, const cgltf_animation& sr
|
||||
}
|
||||
continue;
|
||||
}
|
||||
Entity targetEntity = iter.value();
|
||||
Channel dstChannel;
|
||||
dstChannel.sourceData = samplers + (srcChannel.sampler - srcSamplers);
|
||||
dstChannel.targetEntity = targetEntity;
|
||||
@@ -556,4 +516,52 @@ void AnimatorImpl::applyAnimation(const Channel& channel, float t, size_t prevIn
|
||||
transformManager->setTransform(node, xform);
|
||||
}
|
||||
|
||||
void AnimatorImpl::resetBoneMatrices(FFilamentInstance* instance) {
|
||||
for (const auto& skin : instance->skins) {
|
||||
size_t njoints = skin.joints.size();
|
||||
boneMatrices.resize(njoints);
|
||||
for (const auto& entity : skin.targets) {
|
||||
auto renderable = renderableManager->getInstance(entity);
|
||||
if (renderable) {
|
||||
for (size_t boneIndex = 0; boneIndex < njoints; ++boneIndex) {
|
||||
boneMatrices[boneIndex] = mat4f();
|
||||
}
|
||||
renderableManager->setBones(renderable, boneMatrices.data(), boneMatrices.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimatorImpl::updateBoneMatrices(FFilamentInstance* instance) {
|
||||
assert_invariant(instance->skins.size() == asset->mSkins.size());
|
||||
size_t skinIndex = 0;
|
||||
for (const auto& skin : instance->skins) {
|
||||
const auto& assetSkin = asset->mSkins[skinIndex++];
|
||||
size_t njoints = skin.joints.size();
|
||||
boneMatrices.resize(njoints);
|
||||
for (Entity entity : skin.targets) {
|
||||
auto renderable = renderableManager->getInstance(entity);
|
||||
if (!renderable) {
|
||||
continue;
|
||||
}
|
||||
mat4f inverseGlobalTransform;
|
||||
auto xformable = transformManager->getInstance(entity);
|
||||
if (xformable) {
|
||||
inverseGlobalTransform = inverse(transformManager->getWorldTransform(xformable));
|
||||
}
|
||||
for (size_t boneIndex = 0; boneIndex < njoints; ++boneIndex) {
|
||||
const auto& joint = skin.joints[boneIndex];
|
||||
const mat4f& inverseBindMatrix = assetSkin.inverseBindMatrices[boneIndex];
|
||||
TransformManager::Instance jointInstance = transformManager->getInstance(joint);
|
||||
mat4f globalJointTransform = transformManager->getWorldTransform(jointInstance);
|
||||
boneMatrices[boneIndex] =
|
||||
inverseGlobalTransform *
|
||||
globalJointTransform *
|
||||
inverseBindMatrix;
|
||||
}
|
||||
renderableManager->setBones(renderable, boneMatrices.data(), boneMatrices.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace filament::gltfio
|
||||
|
||||
+181
-90
@@ -63,10 +63,26 @@ namespace filament::gltfio {
|
||||
|
||||
using SceneMask = NodeManager::SceneMask;
|
||||
|
||||
void importSkins(const cgltf_data* gltf, const NodeMap& nodeMap, SkinVector& dstSkins);
|
||||
|
||||
static const auto FREE_CALLBACK = [](void* mem, size_t, void*) { free(mem); };
|
||||
|
||||
// The default glTF material.
|
||||
static constexpr cgltf_material kDefaultMat = {
|
||||
.name = (char*) "Default GLTF material",
|
||||
.has_pbr_metallic_roughness = true,
|
||||
.has_pbr_specular_glossiness = false,
|
||||
.has_clearcoat = false,
|
||||
.has_transmission = false,
|
||||
.has_volume = false,
|
||||
.has_ior = false,
|
||||
.has_specular = false,
|
||||
.has_sheen = false,
|
||||
.pbr_metallic_roughness = {
|
||||
.base_color_factor = {1.0, 1.0, 1.0, 1.0},
|
||||
.metallic_factor = 1.0,
|
||||
.roughness_factor = 1.0,
|
||||
},
|
||||
};
|
||||
|
||||
// Sometimes a glTF bufferview includes unused data at the end (e.g. in skinning.gltf) so we need to
|
||||
// compute the correct size of the vertex buffer. Filament automatically infers the size of
|
||||
// driver-level vertex buffers from the attribute data (stride, count, offset) and clients are
|
||||
@@ -90,6 +106,76 @@ static const char* getNodeName(const cgltf_node* node, const char* defaultNodeNa
|
||||
return defaultNodeName;
|
||||
}
|
||||
|
||||
static bool primitiveHasVertexColor(const cgltf_primitive* inPrim) {
|
||||
for (int slot = 0; slot < inPrim->attributes_count; slot++) {
|
||||
const cgltf_attribute& inputAttribute = inPrim->attributes[slot];
|
||||
if (inputAttribute.type == cgltf_attribute_type_color) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static LightManager::Type getLightType(const cgltf_light_type light) {
|
||||
switch (light) {
|
||||
case cgltf_light_type_max_enum:
|
||||
case cgltf_light_type_invalid:
|
||||
assert_invariant(false && "Invalid light type");
|
||||
return LightManager::Type::DIRECTIONAL;
|
||||
case cgltf_light_type_directional:
|
||||
return LightManager::Type::DIRECTIONAL;
|
||||
case cgltf_light_type_point:
|
||||
return LightManager::Type::POINT;
|
||||
case cgltf_light_type_spot:
|
||||
return LightManager::Type::FOCUSED_SPOT;
|
||||
}
|
||||
}
|
||||
|
||||
// MaterialInstanceCache
|
||||
// ---------------------
|
||||
// Each glTF material definition corresponds to a single MaterialInstance, which are temporarily
|
||||
// cached when loading a FilamentInstance. If a given glTF material is referenced by multiple
|
||||
// glTF meshes, then their corresponding Filament primitives will share the same Filament
|
||||
// MaterialInstance and UvMap. The UvMap is a mapping from each texcoord slot in glTF to one of
|
||||
// Filament's 2 texcoord sets.
|
||||
//
|
||||
// Notes:
|
||||
// - The Material objects (used to create instances) are cached in MaterialProvider, not here.
|
||||
// - The cache is not responsible for destroying material instances. They are owned by the asset.
|
||||
class MaterialInstanceCache {
|
||||
public:
|
||||
struct Entry {
|
||||
MaterialInstance* instance;
|
||||
UvMap uvmap;
|
||||
};
|
||||
|
||||
MaterialInstanceCache() {}
|
||||
|
||||
MaterialInstanceCache(const cgltf_data* hierarchy) :
|
||||
mHierarchy(hierarchy),
|
||||
mMaterialInstances(hierarchy->materials_count, Entry{}),
|
||||
mMaterialInstancesWithVertexColor(hierarchy->materials_count, Entry{}) {}
|
||||
|
||||
Entry* getEntry(const cgltf_material** mat, bool vertexColor) {
|
||||
if (*mat) {
|
||||
EntryVector& entries = vertexColor ?
|
||||
mMaterialInstancesWithVertexColor : mMaterialInstances;
|
||||
const cgltf_material* basePointer = mHierarchy->materials;
|
||||
return &entries[*mat - basePointer];
|
||||
}
|
||||
*mat = &kDefaultMat;
|
||||
return vertexColor ? &mDefaultMaterialInstanceWithVertexColor : &mDefaultMaterialInstance;
|
||||
}
|
||||
|
||||
private:
|
||||
using EntryVector = utils::FixedCapacityVector<Entry>;
|
||||
const cgltf_data* mHierarchy = {};
|
||||
EntryVector mMaterialInstances;
|
||||
EntryVector mMaterialInstancesWithVertexColor;
|
||||
Entry mDefaultMaterialInstance = {};
|
||||
Entry mDefaultMaterialInstanceWithVertexColor = {};
|
||||
};
|
||||
|
||||
struct FAssetLoader : public AssetLoader {
|
||||
FAssetLoader(const AssetConfiguration& config) :
|
||||
mEntityManager(config.entities ? *config.entities : EntityManager::get()),
|
||||
@@ -102,8 +188,10 @@ struct FAssetLoader : public AssetLoader {
|
||||
|
||||
FFilamentAsset* createAsset(const uint8_t* bytes, uint32_t nbytes);
|
||||
FFilamentAsset* createInstancedAsset(const uint8_t* bytes, uint32_t numBytes,
|
||||
FilamentInstance** instances, size_t numInstances);
|
||||
FilamentInstance** instances, size_t numInstances);
|
||||
FilamentInstance* createInstance(FFilamentAsset* primary);
|
||||
void importSkins(FFilamentAsset* primary, FFilamentInstance* instance,
|
||||
const cgltf_data* srcAsset);
|
||||
|
||||
static void destroy(FAssetLoader** loader) noexcept {
|
||||
delete *loader;
|
||||
@@ -130,10 +218,11 @@ struct FAssetLoader : public AssetLoader {
|
||||
return mMaterials.getMaterials();
|
||||
}
|
||||
|
||||
private:
|
||||
void createAsset(const cgltf_data* srcAsset, size_t numInstances);
|
||||
FFilamentInstance* createInstance(FFilamentAsset* primary, const cgltf_data* srcAsset);
|
||||
void createEntity(const cgltf_data* srcAsset, const cgltf_node* node, SceneMask scenes,
|
||||
Entity parent, bool enableLight, FFilamentInstance* instance);
|
||||
void recurseEntities(const cgltf_data* srcAsset, const cgltf_node* node, SceneMask scenes,
|
||||
Entity parent, FFilamentInstance* instance);
|
||||
void createRenderable(const cgltf_data* srcAsset, const cgltf_node* node, Entity entity,
|
||||
const char* name);
|
||||
bool createPrimitive(const cgltf_primitive* inPrim, Primitive* outPrim, const UvMap& uvmap,
|
||||
@@ -144,12 +233,10 @@ struct FAssetLoader : public AssetLoader {
|
||||
const cgltf_material* inputMat, UvMap* uvmap, bool vertexColor);
|
||||
void addTextureBinding(MaterialInstance* materialInstance, const char* parameterName,
|
||||
const cgltf_texture* srcTexture, bool srgb);
|
||||
bool primitiveHasVertexColor(const cgltf_primitive* inPrim) const;
|
||||
void createMaterialVariants(const cgltf_data* srcAsset, const cgltf_mesh* mesh, Entity entity,
|
||||
FFilamentInstance* instance);
|
||||
|
||||
static LightManager::Type getLightType(const cgltf_light_type type);
|
||||
|
||||
public:
|
||||
EntityManager& mEntityManager;
|
||||
RenderableManager& mRenderableManager;
|
||||
NameComponentManager* const mNameManager;
|
||||
@@ -164,6 +251,7 @@ struct FAssetLoader : public AssetLoader {
|
||||
const char* mDefaultNodeName;
|
||||
bool mError = false;
|
||||
bool mDiagnosticsEnabled = false;
|
||||
MaterialInstanceCache mMaterialInstanceCache;
|
||||
|
||||
// Weak reference to the largest dummy buffer so far in the current loading phase.
|
||||
BufferObject* mDummyBufferObject;
|
||||
@@ -228,16 +316,16 @@ FilamentInstance* FAssetLoader::createInstance(FFilamentAsset* primary) {
|
||||
slog.e << "There is no scene in the asset." << io::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mMaterialInstanceCache = MaterialInstanceCache(srcAsset);
|
||||
|
||||
FFilamentInstance* instance = createInstance(primary, srcAsset);
|
||||
|
||||
// Import the skin data. This is normally done by ResourceLoader but dynamically created
|
||||
// instances are a bit special.
|
||||
importSkins(primary->mSourceAsset->hierarchy, instance->nodeMap, instance->skins);
|
||||
if (primary->mAnimator) {
|
||||
primary->mAnimator->addInstance(instance);
|
||||
}
|
||||
|
||||
primary->mDependencyGraph.refinalize();
|
||||
primary->mDependencyGraph.commitEdges();
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -256,6 +344,8 @@ void FAssetLoader::createAsset(const cgltf_data* srcAsset, size_t numInstances)
|
||||
mResult = new FFilamentAsset(&mEngine, mNameManager, &mEntityManager, &mNodeManager, srcAsset);
|
||||
mDummyBufferObject = nullptr;
|
||||
|
||||
mMaterialInstanceCache = MaterialInstanceCache(srcAsset);
|
||||
|
||||
// It is not an error for a glTF file to have zero scenes.
|
||||
mResult->mScenes.clear();
|
||||
if (srcAsset->scenes == nullptr) {
|
||||
@@ -307,7 +397,7 @@ void FAssetLoader::createAsset(const cgltf_data* srcAsset, size_t numInstances)
|
||||
mResult->mRenderableCount = 0;
|
||||
|
||||
// Create a separate entity hierarchy for each instance. Note that MeshCache (vertex
|
||||
// buffers and index buffers) and MatInstanceCache (materials and textures) help avoid
|
||||
// buffers and index buffers) and MaterialInstanceCache (materials and textures) help avoid
|
||||
// needless duplication of resources.
|
||||
for (size_t index = 0; index < numInstances; ++index) {
|
||||
if (createInstance(mResult, srcAsset) == nullptr) {
|
||||
@@ -325,10 +415,10 @@ void FAssetLoader::createAsset(const cgltf_data* srcAsset, size_t numInstances)
|
||||
|
||||
// Find every unique resource URI and store a pointer to any of the cgltf-owned cstrings
|
||||
// that match the URI. These strings get freed during releaseSourceData().
|
||||
tsl::robin_map<std::string_view, const char*> resourceUris;
|
||||
tsl::robin_set<std::string_view> resourceUris;
|
||||
auto addResourceUri = [&resourceUris](const char* uri) {
|
||||
if (uri) {
|
||||
resourceUris[uri] = uri;
|
||||
resourceUris.insert(uri);
|
||||
}
|
||||
};
|
||||
for (cgltf_size i = 0, len = srcAsset->buffers_count; i < len; ++i) {
|
||||
@@ -338,8 +428,8 @@ void FAssetLoader::createAsset(const cgltf_data* srcAsset, size_t numInstances)
|
||||
addResourceUri(srcAsset->images[i].uri);
|
||||
}
|
||||
mResult->mResourceUris.reserve(resourceUris.size());
|
||||
for (const auto& pair : resourceUris) {
|
||||
mResult->mResourceUris.push_back(pair.second);
|
||||
for (std::string_view uri : resourceUris) {
|
||||
mResult->mResourceUris.push_back(uri.data());
|
||||
}
|
||||
|
||||
if (mError) {
|
||||
@@ -356,12 +446,9 @@ FFilamentInstance* FAssetLoader::createInstance(FFilamentAsset* primary,
|
||||
mTransformManager.create(instanceRoot, rootTransform);
|
||||
|
||||
// Create an instance object, which is a just a lightweight wrapper around a vector of
|
||||
// entities and a lazily created animator.
|
||||
FFilamentInstance* instance = new FFilamentInstance;
|
||||
instance->root = instanceRoot;
|
||||
instance->animator = nullptr;
|
||||
instance->owner = primary;
|
||||
primary->mInstances.push_back(instance);
|
||||
// entities and an animator. The creation of animator is triggered from ResourceLoader
|
||||
// because it could require external bin data.
|
||||
FFilamentInstance* instance = new FFilamentInstance(instanceRoot, primary);
|
||||
|
||||
// Check if the asset has variants.
|
||||
instance->variants.reserve(srcAsset->variants_count);
|
||||
@@ -371,13 +458,20 @@ FFilamentInstance* FAssetLoader::createInstance(FFilamentAsset* primary,
|
||||
|
||||
// For each scene root, recursively create all entities.
|
||||
for (const auto& pair : mRootNodes) {
|
||||
createEntity(srcAsset, pair.first, pair.second, instanceRoot, false, instance);
|
||||
recurseEntities(srcAsset, pair.first, pair.second, instanceRoot, instance);
|
||||
}
|
||||
|
||||
importSkins(primary, instance, srcAsset);
|
||||
|
||||
// This needs to stay near the end of the method to allow detection of the first instance.
|
||||
primary->mInstances.push_back(instance);
|
||||
|
||||
instance->boundingBox = primary->mBoundingBox;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void FAssetLoader::createEntity(const cgltf_data* srcAsset, const cgltf_node* node,
|
||||
SceneMask scenes, Entity parent, bool enableLight, FFilamentInstance* instance) {
|
||||
void FAssetLoader::recurseEntities(const cgltf_data* srcAsset, const cgltf_node* node,
|
||||
SceneMask scenes, Entity parent, FFilamentInstance* instance) {
|
||||
NodeManager& nm = mNodeManager;
|
||||
const Entity entity = mEntityManager.create();
|
||||
nm.create(entity);
|
||||
@@ -409,7 +503,7 @@ void FAssetLoader::createEntity(const cgltf_data* srcAsset, const cgltf_node* no
|
||||
// Update the asset's entity list and private node mapping.
|
||||
mResult->mEntities.push_back(entity);
|
||||
instance->entities.push_back(entity);
|
||||
instance->nodeMap[node] = entity;
|
||||
instance->nodeMap[node - srcAsset->nodes] = entity;
|
||||
|
||||
const char* name = getNodeName(node, mDefaultNodeName);
|
||||
|
||||
@@ -432,7 +526,12 @@ void FAssetLoader::createEntity(const cgltf_data* srcAsset, const cgltf_node* no
|
||||
}
|
||||
}
|
||||
|
||||
if (node->light && enableLight) {
|
||||
// TODO: refactor loading into 2 passes: the first pass creates all objects that are shared
|
||||
// amongst instances (VertexBuffer, IndexBuffer, etc) while the second pass creates the
|
||||
// lightweight ECS stuff (Renderables, Transformables) for each instance. This will allow
|
||||
// users to create assets with zero instances, which is a feature request from Google.
|
||||
|
||||
if (node->light && mResult->mInstances.empty()) {
|
||||
createLight(node->light, entity);
|
||||
}
|
||||
|
||||
@@ -441,7 +540,7 @@ void FAssetLoader::createEntity(const cgltf_data* srcAsset, const cgltf_node* no
|
||||
}
|
||||
|
||||
for (cgltf_size i = 0, len = node->children_count; i < len; ++i) {
|
||||
createEntity(srcAsset, node->children[i], scenes, entity, enableLight, instance);
|
||||
recurseEntities(srcAsset, node->children[i], scenes, entity, instance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -453,17 +552,17 @@ void FAssetLoader::createRenderable(const cgltf_data* srcAsset, const cgltf_node
|
||||
auto thisTransform = mTransformManager.getInstance(entity);
|
||||
mat4f worldTransform = mTransformManager.getWorldTransform(thisTransform);
|
||||
|
||||
cgltf_size nprims = mesh->primitives_count;
|
||||
RenderableManager::Builder builder(nprims);
|
||||
const cgltf_size primitiveCount = mesh->primitives_count;
|
||||
RenderableManager::Builder builder(primitiveCount);
|
||||
|
||||
// If the mesh is already loaded, obtain the list of Filament VertexBuffer / IndexBuffer objects
|
||||
// that were already generated (one for each primitive), otherwise allocate a new list of
|
||||
// pointers for the primitives.
|
||||
auto iter = mResult->mMeshCache.find(mesh);
|
||||
if (iter == mResult->mMeshCache.end()) {
|
||||
mResult->mMeshCache[mesh].resize(nprims);
|
||||
FixedCapacityVector<Primitive>& prims = mResult->mMeshCache[mesh - srcAsset->meshes];
|
||||
if (prims.empty()) {
|
||||
prims = FixedCapacityVector<Primitive>(primitiveCount, Primitive {});
|
||||
}
|
||||
Primitive* outputPrim = mResult->mMeshCache[mesh].data();
|
||||
Primitive* outputPrim = prims.data();
|
||||
const cgltf_primitive* inputPrim = &mesh->primitives[0];
|
||||
|
||||
Aabb aabb;
|
||||
@@ -473,7 +572,9 @@ void FAssetLoader::createRenderable(const cgltf_data* srcAsset, const cgltf_node
|
||||
builder.morphing(numMorphTargets);
|
||||
|
||||
// For each prim, create a Filament VertexBuffer, IndexBuffer, and MaterialInstance.
|
||||
for (cgltf_size index = 0; index < nprims; ++index, ++outputPrim, ++inputPrim) {
|
||||
// The VertexBuffer and IndexBuffer objects are cached for possible re-use, but MaterialInstance
|
||||
// is not.
|
||||
for (cgltf_size index = 0; index < primitiveCount; ++index, ++outputPrim, ++inputPrim) {
|
||||
RenderableManager::PrimitiveType primType;
|
||||
if (!getPrimitiveType(inputPrim->type, &primType)) {
|
||||
slog.e << "Unsupported primitive type in " << name << io::endl;
|
||||
@@ -539,8 +640,7 @@ void FAssetLoader::createRenderable(const cgltf_data* srcAsset, const cgltf_node
|
||||
}
|
||||
|
||||
// Per the spec, glTF models must have valid mix / max annotations for position attributes.
|
||||
// However in practice these can be missing and we should be as robust as other glTF viewers.
|
||||
// If desired, clients can enable the "recomputeBoundingBoxes" feature in ResourceLoader.
|
||||
// If desired, clients can call "recomputeBoundingBoxes()" in FilamentInstance.
|
||||
Box box = Box().set(aabb.min, aabb.max);
|
||||
if (box.isEmpty()) {
|
||||
slog.w << "Missing bounding box in " << name << io::endl;
|
||||
@@ -587,7 +687,8 @@ void FAssetLoader::createMaterialVariants(const cgltf_data* srcAsset, const cglt
|
||||
break;
|
||||
}
|
||||
bool hasVertexColor = primitiveHasVertexColor(srcPrim);
|
||||
MaterialInstance* mi = createMaterialInstance(srcAsset, material, &uvmap, hasVertexColor);
|
||||
MaterialInstance* mi =
|
||||
createMaterialInstance(srcAsset, material, &uvmap, hasVertexColor);
|
||||
assert_invariant(mi);
|
||||
if (!mi) {
|
||||
mError = true;
|
||||
@@ -766,7 +867,8 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive* inPrim, Primitive* out
|
||||
cgltf_size targetsCount = inPrim->targets_count;
|
||||
|
||||
if (targetsCount > MAX_MORPH_TARGETS) {
|
||||
utils::slog.w << "WARNING: Exceeded max morph target count of " << MAX_MORPH_TARGETS << utils::io::endl;
|
||||
utils::slog.w << "WARNING: Exceeded max morph target count of "
|
||||
<< MAX_MORPH_TARGETS << utils::io::endl;
|
||||
targetsCount = MAX_MORPH_TARGETS;
|
||||
}
|
||||
|
||||
@@ -982,32 +1084,13 @@ void FAssetLoader::createCamera(const cgltf_camera* camera, Entity entity) {
|
||||
|
||||
MaterialInstance* FAssetLoader::createMaterialInstance(const cgltf_data* srcAsset,
|
||||
const cgltf_material* inputMat, UvMap* uvmap, bool vertexColor) {
|
||||
intptr_t key = ((intptr_t) inputMat) ^ (vertexColor ? 1 : 0);
|
||||
auto iter = mResult->mMatInstanceCache.find(key);
|
||||
if (iter != mResult->mMatInstanceCache.end()) {
|
||||
*uvmap = iter->second.uvmap;
|
||||
return iter->second.instance;
|
||||
MaterialInstanceCache::Entry* const cacheEntry =
|
||||
mMaterialInstanceCache.getEntry(&inputMat, vertexColor);
|
||||
if (cacheEntry->instance) {
|
||||
*uvmap = cacheEntry->uvmap;
|
||||
return cacheEntry->instance;
|
||||
}
|
||||
|
||||
// The default glTF material.
|
||||
static const cgltf_material kDefaultMat = {
|
||||
.name = (char*) "Default GLTF material",
|
||||
.has_pbr_metallic_roughness = true,
|
||||
.has_pbr_specular_glossiness = false,
|
||||
.has_clearcoat = false,
|
||||
.has_transmission = false,
|
||||
.has_volume = false,
|
||||
.has_ior = false,
|
||||
.has_specular = false,
|
||||
.has_sheen = false,
|
||||
.pbr_metallic_roughness = {
|
||||
.base_color_factor = {1.0, 1.0, 1.0, 1.0},
|
||||
.metallic_factor = 1.0,
|
||||
.roughness_factor = 1.0,
|
||||
},
|
||||
};
|
||||
inputMat = inputMat ? inputMat : &kDefaultMat;
|
||||
|
||||
auto mrConfig = inputMat->pbr_metallic_roughness;
|
||||
auto sgConfig = inputMat->pbr_specular_glossiness;
|
||||
auto ccConfig = inputMat->clearcoat;
|
||||
@@ -1123,7 +1206,8 @@ MaterialInstance* FAssetLoader::createMaterialInstance(const cgltf_data* srcAsse
|
||||
mi->setMaskThreshold(inputMat->alpha_cutoff);
|
||||
}
|
||||
|
||||
float3 emissiveFactor(inputMat->emissive_factor[0], inputMat->emissive_factor[1], inputMat->emissive_factor[2]);
|
||||
const float* emissive = &inputMat->emissive_factor[0];
|
||||
float3 emissiveFactor(emissive[0], emissive[1], emissive[2]);
|
||||
if (inputMat->has_emissive_strength) {
|
||||
emissiveFactor *= inputMat->emissive_strength.emissive_strength;
|
||||
}
|
||||
@@ -1211,7 +1295,8 @@ MaterialInstance* FAssetLoader::createMaterialInstance(const cgltf_data* srcAsse
|
||||
}
|
||||
}
|
||||
if (matkey.hasClearCoatRoughnessTexture) {
|
||||
addTextureBinding(mi, "clearCoatRoughnessMap", ccConfig.clearcoat_roughness_texture.texture, false);
|
||||
addTextureBinding(mi, "clearCoatRoughnessMap",
|
||||
ccConfig.clearcoat_roughness_texture.texture, false);
|
||||
if (matkey.hasTextureTransforms) {
|
||||
const cgltf_texture_transform& uvt = ccConfig.clearcoat_roughness_texture.transform;
|
||||
auto uvmat = matrixFromUvTransform(uvt.offset, uvt.rotation, uvt.scale);
|
||||
@@ -1219,7 +1304,8 @@ MaterialInstance* FAssetLoader::createMaterialInstance(const cgltf_data* srcAsse
|
||||
}
|
||||
}
|
||||
if (matkey.hasClearCoatNormalTexture) {
|
||||
addTextureBinding(mi, "clearCoatNormalMap", ccConfig.clearcoat_normal_texture.texture, false);
|
||||
addTextureBinding(mi, "clearCoatNormalMap",
|
||||
ccConfig.clearcoat_normal_texture.texture, false);
|
||||
if (matkey.hasTextureTransforms) {
|
||||
const cgltf_texture_transform& uvt = ccConfig.clearcoat_normal_texture.transform;
|
||||
auto uvmat = matrixFromUvTransform(uvt.offset, uvt.rotation, uvt.scale);
|
||||
@@ -1243,7 +1329,8 @@ MaterialInstance* FAssetLoader::createMaterialInstance(const cgltf_data* srcAsse
|
||||
}
|
||||
}
|
||||
if (matkey.hasSheenRoughnessTexture) {
|
||||
addTextureBinding(mi, "sheenRoughnessMap", shConfig.sheen_roughness_texture.texture, false);
|
||||
addTextureBinding(mi, "sheenRoughnessMap",
|
||||
shConfig.sheen_roughness_texture.texture, false);
|
||||
if (matkey.hasTextureTransforms) {
|
||||
const cgltf_texture_transform& uvt = shConfig.sheen_roughness_texture.transform;
|
||||
auto uvmat = matrixFromUvTransform(uvt.offset, uvt.rotation, uvt.scale);
|
||||
@@ -1303,7 +1390,7 @@ MaterialInstance* FAssetLoader::createMaterialInstance(const cgltf_data* srcAsse
|
||||
inputMat->emissive_strength.emissive_strength : 1.0f);
|
||||
}
|
||||
|
||||
mResult->mMatInstanceCache[key] = {mi, *uvmap};
|
||||
*cacheEntry = { mi, *uvmap };
|
||||
return mi;
|
||||
}
|
||||
|
||||
@@ -1315,6 +1402,7 @@ void FAssetLoader::addTextureBinding(MaterialInstance* materialInstance, const c
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
TextureSampler dstSampler;
|
||||
auto srcSampler = srcTexture->sampler;
|
||||
if (srcSampler) {
|
||||
@@ -1327,43 +1415,46 @@ void FAssetLoader::addTextureBinding(MaterialInstance* materialInstance, const c
|
||||
dstSampler.setWrapModeS(TextureSampler::WrapMode::REPEAT);
|
||||
dstSampler.setWrapModeT(TextureSampler::WrapMode::REPEAT);
|
||||
|
||||
// These defaults are up the implementation but since we try to provide mipmaps,
|
||||
// These defaults are up to the implementation but since we try to provide mipmaps,
|
||||
// we might as well use them. In practice the conformance models look awful without
|
||||
// using mipmapping by default.
|
||||
dstSampler.setMagFilter(TextureSampler::MagFilter::LINEAR);
|
||||
dstSampler.setMinFilter(TextureSampler::MinFilter::LINEAR_MIPMAP_LINEAR);
|
||||
}
|
||||
|
||||
mResult->mTextureSlots.push_back({
|
||||
.texture = srcTexture,
|
||||
const intptr_t textureIndex = srcTexture - mResult->mSourceAsset->hierarchy->textures;
|
||||
|
||||
mResult->addTextureSlot({
|
||||
.sourceTexture = (size_t) textureIndex,
|
||||
.materialInstance = materialInstance,
|
||||
.materialParameter = parameterName,
|
||||
.sampler = dstSampler,
|
||||
.srgb = srgb
|
||||
});
|
||||
mResult->mDependencyGraph.addEdge(materialInstance, parameterName);
|
||||
}
|
||||
|
||||
bool FAssetLoader::primitiveHasVertexColor(const cgltf_primitive* inPrim) const {
|
||||
for (int slot = 0; slot < inPrim->attributes_count; slot++) {
|
||||
const cgltf_attribute& inputAttribute = inPrim->attributes[slot];
|
||||
if (inputAttribute.type == cgltf_attribute_type_color) {
|
||||
return true;
|
||||
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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
for (cgltf_size i = 0, len = gltf->skins_count; i < len; ++i) {
|
||||
FFilamentInstance::Skin& dstSkin = instance->skins[i];
|
||||
const cgltf_skin& srcSkin = gltf->skins[i];
|
||||
|
||||
LightManager::Type FAssetLoader::getLightType(const cgltf_light_type light) {
|
||||
switch (light) {
|
||||
case cgltf_light_type_max_enum:
|
||||
case cgltf_light_type_invalid:
|
||||
case cgltf_light_type_directional:
|
||||
return LightManager::Type::DIRECTIONAL;
|
||||
case cgltf_light_type_point:
|
||||
return LightManager::Type::POINT;
|
||||
case cgltf_light_type_spot:
|
||||
return LightManager::Type::FOCUSED_SPOT;
|
||||
// Build a list of transformables for this skin, one for each joint.
|
||||
dstSkin.joints = FixedCapacityVector<Entity>(srcSkin.joints_count);
|
||||
for (cgltf_size i = 0, len = srcSkin.joints_count; i < len; ++i) {
|
||||
dstSkin.joints[i] = nodeMap[srcSkin.joints[i] - gltf->nodes];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#include "DependencyGraph.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
using namespace filament;
|
||||
using namespace utils;
|
||||
|
||||
@@ -34,38 +36,22 @@ size_t DependencyGraph::popRenderables(Entity* result, size_t count) noexcept {
|
||||
}
|
||||
|
||||
void DependencyGraph::addEdge(Entity entity, MaterialInstance* mi) {
|
||||
|
||||
// Permit adding an Entity-Material edge to a finalized graph as long as the material is already
|
||||
// known. Since we already encountered this material instance, we already know what textures it
|
||||
// is associated with.
|
||||
assert(!mFinalized || mMaterialToEntity.find(mi) != mMaterialToEntity.end());
|
||||
|
||||
mMaterialToEntity[mi].insert(entity);
|
||||
mEntityToMaterial[entity].materials.insert(mi);
|
||||
}
|
||||
|
||||
void DependencyGraph::addEdge(MaterialInstance* mi, const char* parameter) {
|
||||
assert(!mFinalized);
|
||||
if (auto iter = mMaterialToTexture.find(mi); iter != mMaterialToTexture.end()) {
|
||||
const tsl::robin_map<std::string, TextureNode*>& params = iter.value().params;
|
||||
if (params.find(parameter) != params.end()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
mMaterialToTexture[mi].params[parameter] = nullptr;
|
||||
}
|
||||
|
||||
// During finalization, the structure of the glTF is known but we have not yet created texture
|
||||
// objects. Find all non-textured entities and immediately add mark them as ready.
|
||||
void DependencyGraph::finalize() {
|
||||
assert(!mFinalized);
|
||||
for (const auto& pair : mMaterialToEntity) {
|
||||
auto mi = pair.first;
|
||||
if (mMaterialToTexture.find(mi) == mMaterialToTexture.end()) {
|
||||
markAsReady(mi);
|
||||
}
|
||||
}
|
||||
mFinalized = true;
|
||||
}
|
||||
|
||||
void DependencyGraph::refinalize() {
|
||||
assert(mFinalized);
|
||||
for (const auto& pair : mMaterialToEntity) {
|
||||
auto material = pair.first;
|
||||
void DependencyGraph::commitEdges() {
|
||||
for (const auto& [material, entities] : mMaterialToEntity) {
|
||||
if (mMaterialToTexture.find(material) == mMaterialToTexture.end()) {
|
||||
markAsReady(material);
|
||||
} else {
|
||||
@@ -75,7 +61,7 @@ void DependencyGraph::refinalize() {
|
||||
}
|
||||
|
||||
void DependencyGraph::addEdge(Texture* texture, MaterialInstance* material, const char* parameter) {
|
||||
assert(texture && !mFinalized);
|
||||
assert_invariant(texture);
|
||||
mTextureToMaterial[texture].insert(material);
|
||||
mMaterialToTexture.at(material).params.at(parameter) = getStatus(texture);
|
||||
}
|
||||
@@ -85,9 +71,8 @@ void DependencyGraph::checkReadiness(Material* material) {
|
||||
|
||||
// Check this material's texture parameters, there are 5 in the worst case.
|
||||
bool materialIsReady = true;
|
||||
for (const auto& pair : status.params) {
|
||||
assert(pair.second && "Parameter-to-Texture edge is missing.");
|
||||
if (!pair.second->ready) {
|
||||
for (const auto& [name, texture] : status.params) {
|
||||
if (!texture || !texture->ready) {
|
||||
materialIsReady = false;
|
||||
break;
|
||||
}
|
||||
@@ -100,7 +85,7 @@ void DependencyGraph::checkReadiness(Material* material) {
|
||||
}
|
||||
|
||||
void DependencyGraph::markAsReady(Texture* texture) {
|
||||
assert(texture && mFinalized);
|
||||
assert_invariant(texture);
|
||||
mTextureNodes.at(texture)->ready = true;
|
||||
|
||||
// Iterate over the materials associated with this texture to check if any have become ready.
|
||||
@@ -115,7 +100,7 @@ void DependencyGraph::markAsReady(MaterialInstance* material) {
|
||||
auto& entities = mMaterialToEntity.at(material);
|
||||
for (auto entity : entities) {
|
||||
auto& status = mEntityToMaterial.at(entity);
|
||||
assert(status.numReadyMaterials <= status.materials.size());
|
||||
assert_invariant(status.numReadyMaterials <= status.materials.size());
|
||||
if (status.numReadyMaterials == status.materials.size()) {
|
||||
continue;
|
||||
}
|
||||
@@ -126,7 +111,7 @@ void DependencyGraph::markAsReady(MaterialInstance* material) {
|
||||
}
|
||||
|
||||
DependencyGraph::TextureNode* DependencyGraph::getStatus(Texture* texture) {
|
||||
assert(texture);
|
||||
assert_invariant(texture);
|
||||
auto iter = mTextureNodes.find(texture);
|
||||
if (iter == mTextureNodes.end()) {
|
||||
TextureNode* status = (mTextureNodes[texture] = std::make_unique<TextureNode>()).get();
|
||||
|
||||
@@ -55,11 +55,11 @@ namespace filament::gltfio {
|
||||
* Texture Texture Texture
|
||||
*
|
||||
* Note that the left-most entity in the above graph has no textures, so it becomes ready as soon as
|
||||
* finalize is called.
|
||||
* commitEdges is called.
|
||||
*/
|
||||
class DependencyGraph {
|
||||
public:
|
||||
using Material = filament::MaterialInstance;
|
||||
using Material = MaterialInstance;
|
||||
using Entity = utils::Entity;
|
||||
|
||||
// Pops up to "count" ready-to-render entities off the queue.
|
||||
@@ -68,23 +68,16 @@ public:
|
||||
// If "result" is null, returns the number of available entities.
|
||||
size_t popRenderables(Entity* result, size_t count) noexcept;
|
||||
|
||||
// These are called during the initial asset loader phase.
|
||||
void addEdge(Entity entity, Material* material);
|
||||
void addEdge(Material* material, const char* parameter);
|
||||
void addEdge(filament::Texture* texture, Material* material, const char* parameter);
|
||||
void addEdge(Texture* texture, Material* material, const char* parameter);
|
||||
|
||||
// Marks the end of synchronous asset loading.
|
||||
//
|
||||
// At this point, the graph enters a finalized state and all non-textured entities are
|
||||
// immediately marked as "ready". However textures are not yet fully decoded.
|
||||
//
|
||||
// After finalization, the only nodes that can be added to the graph are entities.
|
||||
void finalize();
|
||||
// Commits a set of edges to the graph. This simply triggers a check to see if
|
||||
// any entities are already ready, e.g. if any entities are non-textured.
|
||||
void commitEdges();
|
||||
|
||||
// Marks the given texture as being fully decoded, with all miplevels initialized.
|
||||
//
|
||||
// This can only be called on a finalized graph.
|
||||
void markAsReady(filament::Texture* texture);
|
||||
void markAsReady(Texture* texture);
|
||||
|
||||
// Marks the material as ready, but due to an error.
|
||||
//
|
||||
@@ -92,15 +85,9 @@ public:
|
||||
// dependencies will never become available.
|
||||
void markAsError(Material* material) { markAsReady(material); }
|
||||
|
||||
// Re-checks the readiness of all entities after finalization.
|
||||
//
|
||||
// This exists only to support dynamic instancing. It is slower than finalize() because it
|
||||
// checks the readiness of existing materials.
|
||||
void refinalize();
|
||||
|
||||
private:
|
||||
struct TextureNode {
|
||||
filament::Texture* texture;
|
||||
Texture* texture;
|
||||
bool ready;
|
||||
};
|
||||
|
||||
@@ -115,21 +102,20 @@ private:
|
||||
|
||||
void checkReadiness(Material* material);
|
||||
void markAsReady(Material* material);
|
||||
TextureNode* getStatus(filament::Texture* texture);
|
||||
TextureNode* getStatus(Texture* texture);
|
||||
|
||||
// The following maps contain the directed edges in the graph.
|
||||
tsl::robin_map<Entity, EntityNode, Entity::Hasher> mEntityToMaterial;
|
||||
tsl::robin_map<Material*, tsl::robin_set<Entity, Entity::Hasher>> mMaterialToEntity;
|
||||
tsl::robin_map<Material*, MaterialNode> mMaterialToTexture;
|
||||
tsl::robin_map<filament::Texture*, tsl::robin_set<Material*>> mTextureToMaterial;
|
||||
tsl::robin_map<Texture*, tsl::robin_set<Material*>> mTextureToMaterial;
|
||||
|
||||
// Each texture (and its readiness flag) can be referenced from multiple nodes, so we own
|
||||
// a collection of wrapper objects in the following map. This uses std::unique_ptr to allow
|
||||
// nodes to refer to a texture wrapper using a stable weak pointer.
|
||||
tsl::robin_map<filament::Texture*, std::unique_ptr<TextureNode>> mTextureNodes;
|
||||
tsl::robin_map<Texture*, std::unique_ptr<TextureNode>> mTextureNodes;
|
||||
|
||||
std::queue<Entity> mReadyRenderables;
|
||||
bool mFinalized = false;
|
||||
};
|
||||
|
||||
} // namespace filament::gltfio
|
||||
|
||||
@@ -44,7 +44,6 @@
|
||||
#include "DracoCache.h"
|
||||
#include "FFilamentInstance.h"
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
#include <tsl/htrie_map.h>
|
||||
|
||||
#include <vector>
|
||||
@@ -78,17 +77,20 @@ struct BufferSlot {
|
||||
const cgltf_accessor* accessor;
|
||||
cgltf_attribute_type attribute;
|
||||
int bufferIndex; // for vertex buffer and morph target buffer only
|
||||
filament::VertexBuffer* vertexBuffer;
|
||||
filament::IndexBuffer* indexBuffer;
|
||||
filament::MorphTargetBuffer* morphTargetBuffer;
|
||||
VertexBuffer* vertexBuffer;
|
||||
IndexBuffer* indexBuffer;
|
||||
MorphTargetBuffer* morphTargetBuffer;
|
||||
};
|
||||
|
||||
// Encapsulates a connection between Texture and MaterialInstance.
|
||||
// Stores a connection between Texture and MaterialInstance; consumed by resource loader so that it
|
||||
// can call "setParameter" on the given MaterialInstance after the Texture has been created.
|
||||
// Since material instances are not typically shared between FilamentInstance, the slots are a
|
||||
// unified list across all instances that exist before creation of Texture objects.
|
||||
struct TextureSlot {
|
||||
const cgltf_texture* texture;
|
||||
filament::MaterialInstance* materialInstance;
|
||||
size_t sourceTexture; // index into cgltf_texture
|
||||
MaterialInstance* materialInstance;
|
||||
const char* materialParameter;
|
||||
filament::TextureSampler sampler;
|
||||
TextureSampler sampler;
|
||||
bool srgb;
|
||||
};
|
||||
|
||||
@@ -97,38 +99,26 @@ struct TextureSlot {
|
||||
// If a given glTF mesh is referenced by multiple glTF nodes, then it generates a separate Filament
|
||||
// renderable for each of those nodes. All renderables generated by a given mesh share a common set
|
||||
// of VertexBuffer and IndexBuffer objects. To achieve the sharing behavior, the loader maintains a
|
||||
// small cache. The cache keys are glTF mesh definitions and the cache entries are lists of
|
||||
// primitives, where a "primitive" is a reference to a Filament VertexBuffer and IndexBuffer.
|
||||
// small cache. There is one cache entry per cgltf_mesh. Each entry is a list of primitives, where a
|
||||
// "primitive" is a reference to a Filament VertexBuffer and IndexBuffer.
|
||||
struct Primitive {
|
||||
filament::VertexBuffer* vertices = nullptr;
|
||||
filament::IndexBuffer* indices = nullptr;
|
||||
filament::Aabb aabb; // object-space bounding box
|
||||
VertexBuffer* vertices = nullptr;
|
||||
IndexBuffer* indices = nullptr;
|
||||
Aabb aabb; // object-space bounding box
|
||||
UvMap uvmap; // mapping from each glTF UV set to either UV0 or UV1 (8 bytes)
|
||||
filament::MorphTargetBuffer* targets = nullptr;
|
||||
MorphTargetBuffer* targets = nullptr;
|
||||
};
|
||||
using MeshCache = tsl::robin_map<const cgltf_mesh*, std::vector<Primitive>>;
|
||||
|
||||
// MatInstanceCache
|
||||
// ----------------
|
||||
// Each glTF material definition corresponds to a single filament::MaterialInstance, which are
|
||||
// temporarily cached during loading. The filament::Material objects that are used to create instances are
|
||||
// cached in MaterialProvider. If a given glTF material is referenced by multiple glTF meshes, then
|
||||
// their corresponding filament primitives will share the same Filament MaterialInstance and UvMap.
|
||||
// The UvMap is a mapping from each texcoord slot in glTF to one of Filament's 2 texcoord sets.
|
||||
struct MaterialEntry {
|
||||
filament::MaterialInstance* instance;
|
||||
UvMap uvmap;
|
||||
};
|
||||
using MatInstanceCache = tsl::robin_map<intptr_t, MaterialEntry>;
|
||||
using MeshCache = utils::FixedCapacityVector<utils::FixedCapacityVector<Primitive>>;
|
||||
|
||||
struct FFilamentAsset : public FilamentAsset {
|
||||
FFilamentAsset(filament::Engine* engine, utils::NameComponentManager* names,
|
||||
FFilamentAsset(Engine* engine, utils::NameComponentManager* names,
|
||||
utils::EntityManager* entityManager, NodeManager* nodeManager,
|
||||
const cgltf_data* srcAsset) :
|
||||
mEngine(engine), mNameManager(names), mEntityManager(entityManager),
|
||||
mNodeManager(nodeManager) {
|
||||
mSourceAsset.reset(new SourceAsset {(cgltf_data*)srcAsset});
|
||||
}
|
||||
mNodeManager(nodeManager),
|
||||
mSourceAsset(new SourceAsset {(cgltf_data*)srcAsset}),
|
||||
mTextureBindings(srcAsset->textures_count, 0),
|
||||
mMeshCache(srcAsset->meshes_count) {}
|
||||
|
||||
~FFilamentAsset();
|
||||
|
||||
@@ -176,11 +166,11 @@ struct FFilamentAsset : public FilamentAsset {
|
||||
return mMaterialInstances.size();
|
||||
}
|
||||
|
||||
const filament::MaterialInstance* const* getMaterialInstances() const noexcept {
|
||||
const MaterialInstance* const* getMaterialInstances() const noexcept {
|
||||
return mMaterialInstances.data();
|
||||
}
|
||||
|
||||
filament::MaterialInstance* const* getMaterialInstances() noexcept {
|
||||
MaterialInstance* const* getMaterialInstances() noexcept {
|
||||
return mMaterialInstances.data();
|
||||
}
|
||||
|
||||
@@ -192,7 +182,7 @@ struct FFilamentAsset : public FilamentAsset {
|
||||
return mResourceUris.data();
|
||||
}
|
||||
|
||||
filament::Aabb getBoundingBox() const noexcept {
|
||||
Aabb getBoundingBox() const noexcept {
|
||||
return mBoundingBox;
|
||||
}
|
||||
|
||||
@@ -222,7 +212,7 @@ struct FFilamentAsset : public FilamentAsset {
|
||||
|
||||
utils::Entity getWireframe() noexcept;
|
||||
|
||||
filament::Engine* getEngine() const noexcept {
|
||||
Engine* getEngine() const noexcept {
|
||||
return mEngine;
|
||||
}
|
||||
|
||||
@@ -246,7 +236,7 @@ struct FFilamentAsset : public FilamentAsset {
|
||||
return mScenes[sceneIndex].c_str();
|
||||
}
|
||||
|
||||
void addEntitiesToScene(filament::Scene& targetScene, const Entity* entities, size_t count,
|
||||
void addEntitiesToScene(Scene& targetScene, const Entity* entities, size_t count,
|
||||
SceneMask sceneFilter);
|
||||
|
||||
void detachFilamentComponents() noexcept {
|
||||
@@ -259,19 +249,41 @@ struct FFilamentAsset : public FilamentAsset {
|
||||
|
||||
// end public API
|
||||
|
||||
void attachTexture(filament::Texture* texture) {
|
||||
void attachTexture(Texture* texture) {
|
||||
mTextures.push_back(texture);
|
||||
}
|
||||
|
||||
void bindTexture(const TextureSlot& tb, filament::Texture* texture) {
|
||||
void bindTexture(const TextureSlot& tb, Texture* texture) {
|
||||
assert_invariant(texture);
|
||||
tb.materialInstance->setParameter(tb.materialParameter, texture, tb.sampler);
|
||||
mDependencyGraph.addEdge(texture, tb.materialInstance, tb.materialParameter);
|
||||
mTextureBindings[tb.sourceTexture] = texture;
|
||||
}
|
||||
|
||||
// If a Filament Texture already exists for the given slot, go ahead and bind it to the
|
||||
// given material instance. (This can occur when creating new FilamentInstances)
|
||||
// Otherwise, stash the binding information so that ResourceLoader can trigger the bind.
|
||||
void addTextureSlot(const TextureSlot& tb) {
|
||||
if (mResourcesLoaded) {
|
||||
Texture* texture = mTextureBindings[tb.sourceTexture];
|
||||
assert_invariant(texture);
|
||||
tb.materialInstance->setParameter(tb.materialParameter, texture, tb.sampler);
|
||||
// Intentionally omit adding a dependency graph edge here. We do not need progressive
|
||||
// reveal for multiple FilamentInstance.
|
||||
} else {
|
||||
mTextureSlots.push_back(tb);
|
||||
mDependencyGraph.addEdge(tb.materialInstance, tb.materialParameter);
|
||||
}
|
||||
}
|
||||
|
||||
void createAnimators();
|
||||
|
||||
filament::Engine* const mEngine;
|
||||
struct Skin {
|
||||
utils::CString name;
|
||||
utils::FixedCapacityVector<math::mat4f> inverseBindMatrices;
|
||||
};
|
||||
|
||||
Engine* const mEngine;
|
||||
utils::NameComponentManager* const mNameManager;
|
||||
utils::EntityManager* const mEntityManager;
|
||||
NodeManager* const mNodeManager;
|
||||
@@ -279,15 +291,16 @@ struct FFilamentAsset : public FilamentAsset {
|
||||
std::vector<utils::Entity> mLightEntities;
|
||||
std::vector<utils::Entity> mCameraEntities;
|
||||
size_t mRenderableCount = 0;
|
||||
std::vector<filament::MaterialInstance*> mMaterialInstances;
|
||||
std::vector<filament::VertexBuffer*> mVertexBuffers;
|
||||
std::vector<filament::BufferObject*> mBufferObjects;
|
||||
std::vector<filament::IndexBuffer*> mIndexBuffers;
|
||||
std::vector<filament::MorphTargetBuffer*> mMorphTargetBuffers;
|
||||
std::vector<filament::Texture*> mTextures;
|
||||
std::vector<MaterialInstance*> mMaterialInstances;
|
||||
std::vector<VertexBuffer*> mVertexBuffers;
|
||||
std::vector<BufferObject*> mBufferObjects;
|
||||
std::vector<IndexBuffer*> mIndexBuffers;
|
||||
std::vector<MorphTargetBuffer*> mMorphTargetBuffers;
|
||||
std::vector<Texture*> mTextures;
|
||||
utils::FixedCapacityVector<Skin> mSkins;
|
||||
utils::FixedCapacityVector<Variant> mVariants;
|
||||
utils::FixedCapacityVector<utils::CString> mScenes;
|
||||
filament::Aabb mBoundingBox;
|
||||
Aabb mBoundingBox;
|
||||
utils::Entity mRoot;
|
||||
std::vector<FFilamentInstance*> mInstances;
|
||||
Animator* mAnimator = nullptr;
|
||||
@@ -320,13 +333,19 @@ struct FFilamentAsset : public FilamentAsset {
|
||||
using SourceHandle = std::shared_ptr<SourceAsset>;
|
||||
SourceHandle mSourceAsset;
|
||||
|
||||
// Transient source data that can freed via releaseSourceData:
|
||||
// Mapping from cgltf_texture to Texture* is required when creating new instances.
|
||||
utils::FixedCapacityVector<Texture*> mTextureBindings;
|
||||
|
||||
// Resource URIs can be queried by the end user.
|
||||
utils::FixedCapacityVector<const char*> mResourceUris;
|
||||
|
||||
// The mapping from cgltf_mesh to VertexBuffer* (etc) is required when creating new instances.
|
||||
MeshCache mMeshCache;
|
||||
|
||||
// Asset information that is produced by AssetLoader and consumed by ResourceLoader:
|
||||
std::vector<BufferSlot> mBufferSlots;
|
||||
std::vector<TextureSlot> mTextureSlots;
|
||||
std::vector<const char*> mResourceUris;
|
||||
std::vector<std::pair<const cgltf_primitive*, filament::VertexBuffer*> > mPrimitives;
|
||||
MatInstanceCache mMatInstanceCache;
|
||||
MeshCache mMeshCache;
|
||||
std::vector<std::pair<const cgltf_primitive*, VertexBuffer*> > mPrimitives;
|
||||
};
|
||||
|
||||
FILAMENT_UPCAST(FilamentAsset)
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
#include <math/mat4.h>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
#include <tsl/robin_set.h>
|
||||
|
||||
#include <vector>
|
||||
@@ -43,25 +42,10 @@ namespace filament::gltfio {
|
||||
struct FFilamentAsset;
|
||||
class Animator;
|
||||
|
||||
struct Skin {
|
||||
utils::CString name;
|
||||
|
||||
// The inverse bind matrices and joints arrays must have the same count. Each element
|
||||
// corresponds to a single bone. We considered using the ECS to store these, but this would be
|
||||
// complicated because a single node might be used as a bone in more than one skin, and its
|
||||
// inverse bind matrix might be unique in each of these skins.
|
||||
utils::FixedCapacityVector<filament::math::mat4f> inverseBindMatrices;
|
||||
utils::FixedCapacityVector<utils::Entity> joints;
|
||||
|
||||
// The set of all nodes that are influenced by this skin.
|
||||
// This is initially gleaned from the glTF file using the "skin" attribute of each node.
|
||||
tsl::robin_set<utils::Entity, utils::Entity::Hasher> targets;
|
||||
};
|
||||
|
||||
struct VariantMapping {
|
||||
utils::Entity renderable;
|
||||
size_t primitiveIndex;
|
||||
filament::MaterialInstance* material;
|
||||
MaterialInstance* material;
|
||||
};
|
||||
|
||||
struct Variant {
|
||||
@@ -69,17 +53,37 @@ struct Variant {
|
||||
std::vector<VariantMapping> mappings;
|
||||
};
|
||||
|
||||
using SkinVector = std::vector<Skin>;
|
||||
using NodeMap = tsl::robin_map<const cgltf_node*, utils::Entity>;
|
||||
|
||||
struct FFilamentInstance : public FilamentInstance {
|
||||
FFilamentInstance(utils::Entity root, FFilamentAsset* owner);
|
||||
|
||||
// The per-instance skin structure caches information to allow animation to be applied
|
||||
// efficiently at run time. Note that shared immutable data, such as the skin name and inverse
|
||||
// bind transforms, are stored in FFilamentAsset.
|
||||
struct Skin {
|
||||
// The list of entities whose transform components define the joints of the skin.
|
||||
utils::FixedCapacityVector<utils::Entity> joints;
|
||||
|
||||
// The set of all entities that are influenced by this skin.
|
||||
// This is initially derived from the glTF, but users can dynamically add or remove targets.
|
||||
tsl::robin_set<utils::Entity, utils::Entity::Hasher> targets;
|
||||
};
|
||||
|
||||
const utils::Entity root;
|
||||
FFilamentAsset* const owner;
|
||||
|
||||
std::vector<utils::Entity> entities;
|
||||
utils::FixedCapacityVector<Variant> variants;
|
||||
utils::Entity root;
|
||||
Animator* animator;
|
||||
FFilamentAsset* owner;
|
||||
SkinVector skins;
|
||||
NodeMap nodeMap;
|
||||
Animator* animator = nullptr;
|
||||
utils::FixedCapacityVector<Skin> skins;
|
||||
|
||||
// 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.
|
||||
// This is discarded after the animator is created.
|
||||
utils::FixedCapacityVector<utils::Entity> nodeMap;
|
||||
|
||||
Aabb boundingBox;
|
||||
void createAnimator();
|
||||
Animator* getAnimator() const noexcept;
|
||||
size_t getSkinCount() const noexcept;
|
||||
@@ -89,6 +93,7 @@ struct FFilamentInstance : public FilamentInstance {
|
||||
void attachSkin(size_t skinIndex, utils::Entity target) noexcept;
|
||||
void detachSkin(size_t skinIndex, utils::Entity target) noexcept;
|
||||
void applyMaterialVariant(size_t variantIndex) noexcept;
|
||||
void recomputeBoundingBoxes();
|
||||
};
|
||||
|
||||
FILAMENT_UPCAST(FilamentInstance)
|
||||
|
||||
@@ -160,18 +160,13 @@ Entity FFilamentAsset::getWireframe() noexcept {
|
||||
|
||||
void FFilamentAsset::releaseSourceData() noexcept {
|
||||
// To ensure that all possible memory is freed, we reassign to new containers rather than
|
||||
// calling clear(). With many container types (such as robin_map), clearing is a fast
|
||||
// operation that merely frees the storage for the items.
|
||||
mMatInstanceCache = {};
|
||||
// calling clear(). With many container types, clearing is a fast operation that merely frees
|
||||
// the storage for the items but not the actual container.
|
||||
mTextureBindings = {};
|
||||
mTextureSlots = {};
|
||||
mMeshCache = {};
|
||||
mResourceUris = {};
|
||||
mPrimitives = {};
|
||||
mBufferSlots = {};
|
||||
mTextureSlots = {};
|
||||
mSourceAsset.reset();
|
||||
for (FFilamentInstance* instance : mInstances) {
|
||||
instance->nodeMap = {};
|
||||
}
|
||||
}
|
||||
|
||||
const char* FFilamentAsset::getName(utils::Entity entity) const noexcept {
|
||||
|
||||
@@ -19,13 +19,20 @@
|
||||
|
||||
#include <gltfio/Animator.h>
|
||||
|
||||
#include <utils/JobSystem.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
using namespace filament;
|
||||
using namespace filament::math;
|
||||
using namespace utils;
|
||||
|
||||
namespace filament::gltfio {
|
||||
|
||||
FFilamentInstance::FFilamentInstance(Entity root, FFilamentAsset* owner) :
|
||||
root(root),
|
||||
owner(owner),
|
||||
nodeMap(owner->mSourceAsset->hierarchy->nodes_count, Entity()) {}
|
||||
|
||||
Animator* FFilamentInstance::getAnimator() const noexcept {
|
||||
assert_invariant(animator);
|
||||
return animator;
|
||||
@@ -34,6 +41,7 @@ Animator* FFilamentInstance::getAnimator() const noexcept {
|
||||
void FFilamentInstance::createAnimator() {
|
||||
assert_invariant(animator == nullptr);
|
||||
animator = new Animator(owner, this);
|
||||
nodeMap = {};
|
||||
}
|
||||
|
||||
size_t FFilamentInstance::getSkinCount() const noexcept {
|
||||
@@ -44,7 +52,7 @@ const char* FFilamentInstance::getSkinNameAt(size_t skinIndex) const noexcept {
|
||||
if (skins.size() <= skinIndex) {
|
||||
return nullptr;
|
||||
}
|
||||
return skins[skinIndex].name.c_str();
|
||||
return owner->mSkins[skinIndex].name.c_str();
|
||||
}
|
||||
|
||||
size_t FFilamentInstance::getJointCountAt(size_t skinIndex) const noexcept {
|
||||
@@ -87,6 +95,194 @@ void FFilamentInstance::applyMaterialVariant(size_t variantIndex) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
void FFilamentInstance::recomputeBoundingBoxes() {
|
||||
ASSERT_PRECONDITION(owner->mSourceAsset,
|
||||
"Do not call releaseSourceData before recomputeBoundingBoxes");
|
||||
|
||||
ASSERT_PRECONDITION(owner->mResourcesLoaded,
|
||||
"Do not call recomputeBoundingBoxes before loadResources or asyncBeginLoad");
|
||||
|
||||
auto& rm = owner->mEngine->getRenderableManager();
|
||||
auto& tm = owner->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());
|
||||
utils::FixedCapacityVector<Entity> modelRoots(tm.getChildCount(root));
|
||||
tm.getChildren(root, modelRoots.data(), modelRoots.size());
|
||||
for (auto e : modelRoots) {
|
||||
tm.setParent(tm.getInstance(e), 0);
|
||||
}
|
||||
|
||||
struct Prim {
|
||||
cgltf_primitive const* prim;
|
||||
Entity node;
|
||||
ssize_t skinIndex;
|
||||
};
|
||||
|
||||
auto computeBoundingBox = [](const cgltf_primitive* prim) -> Aabb {
|
||||
Aabb aabb;
|
||||
for (cgltf_size slot = 0; slot < prim->attributes_count; slot++) {
|
||||
const cgltf_attribute& attr = prim->attributes[slot];
|
||||
const cgltf_accessor* accessor = attr.data;
|
||||
const size_t dim = cgltf_num_components(accessor->type);
|
||||
if (attr.type == cgltf_attribute_type_position && dim >= 3) {
|
||||
utils::FixedCapacityVector<float> unpacked(accessor->count * dim);
|
||||
cgltf_accessor_unpack_floats(accessor, unpacked.data(), unpacked.size());
|
||||
for (cgltf_size i = 0, j = 0, n = accessor->count; i < n; ++i, j += dim) {
|
||||
float3 pt(unpacked[j + 0], unpacked[j + 1], unpacked[j + 2]);
|
||||
aabb.min = min(aabb.min, pt);
|
||||
aabb.max = max(aabb.max, pt);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return aabb;
|
||||
};
|
||||
|
||||
auto computeBoundingBoxSkinned = [&tm, this](const Prim& prim) -> Aabb {
|
||||
FixedCapacityVector<float3> verts;
|
||||
FixedCapacityVector<uint4> joints;
|
||||
FixedCapacityVector<float4> weights;
|
||||
for (cgltf_size slot = 0, n = prim.prim->attributes_count; slot < n; ++slot) {
|
||||
const cgltf_attribute& attr = prim.prim->attributes[slot];
|
||||
const cgltf_accessor& accessor = *attr.data;
|
||||
switch (attr.type) {
|
||||
case cgltf_attribute_type_position:
|
||||
verts = FixedCapacityVector<float3>(accessor.count);
|
||||
cgltf_accessor_unpack_floats(&accessor, &verts.data()->x, accessor.count * 3);
|
||||
break;
|
||||
case cgltf_attribute_type_joints: {
|
||||
FixedCapacityVector<float4> tmp(accessor.count);
|
||||
cgltf_accessor_unpack_floats(&accessor, &tmp.data()->x, accessor.count * 4);
|
||||
joints = FixedCapacityVector<uint4>(accessor.count);
|
||||
for (size_t i = 0, n = accessor.count; i < n; ++i) {
|
||||
joints[i] = uint4(tmp[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cgltf_attribute_type_weights:
|
||||
weights = FixedCapacityVector<float4>(accessor.count);
|
||||
cgltf_accessor_unpack_floats(&accessor, &weights.data()->x, accessor.count * 4);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
for (size_t i = 0, n = verts.size(); i < n; i++) {
|
||||
mat4f tmp = mat4f(0.0f);
|
||||
for (size_t j = 0; j < 4; j++) {
|
||||
size_t jointIndex = joints[i][j];
|
||||
Entity jointEntity = instanceSkin.joints[jointIndex];
|
||||
mat4f globalJointTransform = tm.getWorldTransform(tm.getInstance(jointEntity));
|
||||
mat4f inverseBindMatrix = assetSkin.inverseBindMatrices[jointIndex];
|
||||
tmp += weights[i][j] * globalJointTransform * inverseBindMatrix;
|
||||
}
|
||||
const mat4f skinMatrix = inverseGlobalTransform * tmp;
|
||||
|
||||
const float3 point = verts[i];
|
||||
|
||||
// NOTE: Filament's vertex shader assumes that last row is [0,0,0,1]
|
||||
// so we make the same assumption in the following transformation.
|
||||
|
||||
const float3 skinnedPoint =
|
||||
point.x * skinMatrix[0].xyz +
|
||||
point.y * skinMatrix[1].xyz +
|
||||
point.z * skinMatrix[2].xyz +
|
||||
skinMatrix[3].xyz;
|
||||
|
||||
aabb.min = min(aabb.min, skinnedPoint);
|
||||
aabb.max = max(aabb.max, skinnedPoint);
|
||||
}
|
||||
return aabb;
|
||||
};
|
||||
|
||||
// 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_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) {
|
||||
primCount += mesh->primitives_count;
|
||||
}
|
||||
}
|
||||
auto primitives = FixedCapacityVector<Prim>::with_capacity(primCount);
|
||||
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];
|
||||
if (const cgltf_mesh* mesh = node.mesh; mesh) {
|
||||
for (cgltf_size j = 0, nprims = mesh->primitives_count; j < nprims; ++j) {
|
||||
primitives.push_back({
|
||||
.prim = &mesh->primitives[j],
|
||||
.node = entity,
|
||||
.skinIndex = node.skin ? (node.skin - baseSkin) : -1
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Kick off a bounding box job for every primitive.
|
||||
FixedCapacityVector<Aabb> bounds(primitives.size());
|
||||
JobSystem& js = owner->mEngine->getJobSystem();
|
||||
JobSystem::Job* parent = js.createJob();
|
||||
for (size_t i = 0; i < primitives.size(); ++i) {
|
||||
Aabb& result = bounds[i];
|
||||
const Prim& prim = primitives[i];
|
||||
if (primitives[i].skinIndex < 0) {
|
||||
js.run(jobs::createJob(js, parent, [&prim, &result, computeBoundingBox] {
|
||||
result = computeBoundingBox(prim.prim);
|
||||
}));
|
||||
} else {
|
||||
js.run(jobs::createJob(js, parent, [&prim, &result, computeBoundingBoxSkinned] {
|
||||
result = computeBoundingBoxSkinned(prim);
|
||||
}));
|
||||
}
|
||||
}
|
||||
js.runAndWait(parent);
|
||||
|
||||
// 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) {
|
||||
const cgltf_node& node = nodes[i];
|
||||
const Entity entity = nodeMap[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;
|
||||
for (cgltf_size j = 0, nprims = mesh->primitives_count; j < nprims; ++j) {
|
||||
Aabb primBounds = bounds[primIndex++];
|
||||
aabb.min = min(aabb.min, primBounds.min);
|
||||
aabb.max = max(aabb.max, primBounds.max);
|
||||
}
|
||||
auto renderable = rm.getInstance(entity);
|
||||
rm.setAxisAlignedBoundingBox(renderable, Box().set(aabb.min, aabb.max));
|
||||
|
||||
// Transform this bounding box, then update the asset-level bounding box.
|
||||
auto transformable = tm.getInstance(entity);
|
||||
const mat4f worldTransform = tm.getWorldTransform(transformable);
|
||||
const Aabb transformed = aabb.transform(worldTransform);
|
||||
assetBounds.min = min(assetBounds.min, transformed.min);
|
||||
assetBounds.max = max(assetBounds.max, transformed.max);
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the root node.
|
||||
for (auto e : modelRoots) {
|
||||
tm.setParent(tm.getInstance(e), root);
|
||||
}
|
||||
|
||||
boundingBox = assetBounds;
|
||||
}
|
||||
|
||||
FilamentAsset* FilamentInstance::getAsset() const noexcept {
|
||||
return upcast(this)->owner;
|
||||
}
|
||||
@@ -136,5 +332,12 @@ void FilamentInstance::detachSkin(size_t skinIndex, Entity target) noexcept {
|
||||
return upcast(this)->detachSkin(skinIndex, target);
|
||||
}
|
||||
|
||||
void FilamentInstance::recomputeBoundingBoxes() {
|
||||
return upcast(this)->recomputeBoundingBoxes();
|
||||
}
|
||||
|
||||
Aabb FilamentInstance::getBoundingBox() const noexcept {
|
||||
return upcast(this)->boundingBox;
|
||||
}
|
||||
|
||||
} // namespace filament::gltfio
|
||||
|
||||
@@ -67,15 +67,11 @@ struct ResourceLoader::Impl {
|
||||
Impl(const ResourceConfiguration& config) :
|
||||
mEngine(config.engine),
|
||||
mNormalizeSkinningWeights(config.normalizeSkinningWeights),
|
||||
mRecomputeBoundingBoxes(config.recomputeBoundingBoxes),
|
||||
mGltfPath(config.gltfPath ? config.gltfPath : ""),
|
||||
mIgnoreBindTransform(config.ignoreBindTransform) {}
|
||||
mGltfPath(config.gltfPath ? config.gltfPath : "") {}
|
||||
|
||||
Engine* const mEngine;
|
||||
const bool mNormalizeSkinningWeights;
|
||||
const bool mRecomputeBoundingBoxes;
|
||||
const std::string mGltfPath;
|
||||
const bool mIgnoreBindTransform;
|
||||
|
||||
// User-provided resource data with URI string keys, populated with addResourceData().
|
||||
// This is used on platforms without traditional file systems, such as Android, iOS, and WebGL.
|
||||
@@ -117,47 +113,6 @@ static void uploadCallback(void* buffer, size_t size, void* user) {
|
||||
delete event;
|
||||
}
|
||||
|
||||
void importSkins(const cgltf_data* gltf, const NodeMap& nodeMap, SkinVector& dstSkins) {
|
||||
dstSkins.resize(gltf->skins_count);
|
||||
for (cgltf_size i = 0, len = gltf->nodes_count; i < len; ++i) {
|
||||
const cgltf_node& node = gltf->nodes[i];
|
||||
if (node.skin) {
|
||||
int skinIndex = node.skin - &gltf->skins[0];
|
||||
Entity entity = nodeMap.at(&node);
|
||||
dstSkins[skinIndex].targets.insert(entity);
|
||||
}
|
||||
}
|
||||
for (cgltf_size i = 0, len = gltf->skins_count; i < len; ++i) {
|
||||
Skin& dstSkin = dstSkins[i];
|
||||
const cgltf_skin& srcSkin = gltf->skins[i];
|
||||
if (srcSkin.name) {
|
||||
dstSkin.name = CString(srcSkin.name);
|
||||
}
|
||||
|
||||
// Build a list of transformables for this skin, one for each joint.
|
||||
dstSkin.joints = FixedCapacityVector<Entity>(srcSkin.joints_count);
|
||||
for (cgltf_size i = 0, len = srcSkin.joints_count; i < len; ++i) {
|
||||
auto iter = nodeMap.find(srcSkin.joints[i]);
|
||||
assert_invariant(iter != nodeMap.end());
|
||||
dstSkin.joints[i] = iter->second;
|
||||
}
|
||||
|
||||
// Retain a copy of the inverse bind matrices because the source blob could be evicted later.
|
||||
const cgltf_accessor* srcMatrices = srcSkin.inverse_bind_matrices;
|
||||
dstSkin.inverseBindMatrices = FixedCapacityVector<mat4f>(srcSkin.joints_count);
|
||||
if (srcMatrices) {
|
||||
auto dstMatrices = (uint8_t*) dstSkin.inverseBindMatrices.data();
|
||||
uint8_t* bytes = (uint8_t*) srcMatrices->buffer_view->buffer->data;
|
||||
if (!bytes) {
|
||||
slog.w << "Empty animation buffer, have resources been loaded yet?" << io::endl;
|
||||
continue;
|
||||
}
|
||||
auto srcBuffer = (void*) (bytes + srcMatrices->offset + srcMatrices->buffer_view->offset);
|
||||
memcpy(dstMatrices, srcBuffer, srcSkin.joints_count * sizeof(mat4f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void convertBytesToShorts(uint16_t* dst, const uint8_t* src, size_t count) {
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
dst[i] = src[i];
|
||||
@@ -212,8 +167,7 @@ static void decodeDracoMeshes(FFilamentAsset* asset) {
|
||||
};
|
||||
|
||||
// Go through every primitive and check if it has a Draco mesh.
|
||||
for (auto& pair : asset->mPrimitives) {
|
||||
const cgltf_primitive* prim = pair.first;
|
||||
for (auto& [prim, vertexBuffer] : asset->mPrimitives) {
|
||||
if (!prim->has_draco_mesh_compression) {
|
||||
continue;
|
||||
}
|
||||
@@ -222,7 +176,6 @@ static void decodeDracoMeshes(FFilamentAsset* asset) {
|
||||
|
||||
// If an error occurs, we can simply set the primitive's associated VertexBuffer to null.
|
||||
// This does not cause a leak because it is a weak reference.
|
||||
auto& vertexBuffer = pair.second;
|
||||
|
||||
// Check if we have already decoded this mesh.
|
||||
DracoMesh* mesh = dracoCache->findOrCreateMesh(draco.buffer_view);
|
||||
@@ -433,23 +386,35 @@ bool ResourceLoader::loadResources(FFilamentAsset* asset, bool async) {
|
||||
// tangent generation.
|
||||
decodeDracoMeshes(asset);
|
||||
|
||||
// Normalize skinning weights, then "import" each skin into the asset by building a mapping of
|
||||
// skins to their affected entities.
|
||||
// For each skin, optionally normalize skinning weights and store a copy of the bind matrices.
|
||||
if (gltf->skins_count > 0) {
|
||||
if (pImpl->mNormalizeSkinningWeights) {
|
||||
normalizeSkinningWeights(asset);
|
||||
}
|
||||
// NOTE: This takes care of up-front instances, but dynamically added instances also
|
||||
// need to import the skin data, which is done in AssetLoader.
|
||||
for (FFilamentInstance* instance : asset->mInstances) {
|
||||
importSkins(gltf, instance->nodeMap, instance->skins);
|
||||
asset->mSkins.reserve(gltf->skins_count);
|
||||
for (cgltf_size i = 0, len = gltf->skins_count; i < len; ++i) {
|
||||
const cgltf_skin& srcSkin = gltf->skins[i];
|
||||
CString name;
|
||||
if (srcSkin.name) {
|
||||
name = CString(srcSkin.name);
|
||||
}
|
||||
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;
|
||||
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));
|
||||
}
|
||||
FFilamentAsset::Skin skin {
|
||||
.name = std::move(name),
|
||||
.inverseBindMatrices = std::move(inverseBindMatrices),
|
||||
};
|
||||
asset->mSkins.emplace_back(std::move(skin));
|
||||
}
|
||||
}
|
||||
|
||||
if (pImpl->mRecomputeBoundingBoxes) {
|
||||
updateBoundingBoxes(asset);
|
||||
}
|
||||
|
||||
Engine& engine = *pImpl->mEngine;
|
||||
|
||||
// Upload VertexBuffer and IndexBuffer data to the GPU.
|
||||
@@ -527,6 +492,9 @@ bool ResourceLoader::loadResources(FFilamentAsset* asset, bool async) {
|
||||
// we need to generate the contents of a GPU buffer by processing one or more CPU buffer(s).
|
||||
pImpl->computeTangents(asset);
|
||||
|
||||
asset->mBufferSlots = {};
|
||||
asset->mPrimitives = {};
|
||||
|
||||
// If any decoding jobs are still underway from a previous load, wait for them to finish.
|
||||
for (const auto& iter : pImpl->mTextureProviders) {
|
||||
iter.second->waitForCompletion();
|
||||
@@ -537,8 +505,8 @@ bool ResourceLoader::loadResources(FFilamentAsset* asset, bool async) {
|
||||
pImpl->createTextures(asset, async);
|
||||
|
||||
// Non-textured renderables are now considered ready, and we can guarantee that no new
|
||||
// materials or textures will be added. notify the dependency graph.
|
||||
asset->mDependencyGraph.finalize();
|
||||
// materials or textures will be added. Notify the dependency graph.
|
||||
asset->mDependencyGraph.commitEdges();
|
||||
|
||||
asset->createAnimators();
|
||||
|
||||
@@ -587,9 +555,9 @@ void ResourceLoader::asyncUpdateLoad() {
|
||||
}
|
||||
|
||||
Texture* ResourceLoader::Impl::getOrCreateTexture(FFilamentAsset* asset, const TextureSlot& tb) {
|
||||
const cgltf_texture* srcTexture = tb.texture;
|
||||
const cgltf_image* image = srcTexture->basisu_image ?
|
||||
srcTexture->basisu_image : srcTexture->image;
|
||||
const cgltf_texture& srcTexture = asset->mSourceAsset->hierarchy->textures[tb.sourceTexture];
|
||||
const cgltf_image* image = srcTexture.basisu_image ?
|
||||
srcTexture.basisu_image : srcTexture.image;
|
||||
const cgltf_buffer_view* bv = image->buffer_view;
|
||||
const char* uri = image->uri;
|
||||
|
||||
@@ -686,7 +654,7 @@ Texture* ResourceLoader::Impl::getOrCreateTexture(FFilamentAsset* asset, const T
|
||||
}
|
||||
|
||||
if (!texture) {
|
||||
const char* name = srcTexture->name ? srcTexture->name : uri;
|
||||
const char* name = srcTexture.name ? srcTexture.name : uri;
|
||||
slog.e << "Unable to create texture " << name << ": "
|
||||
<< provider->getPushMessage() << io::endl;
|
||||
asset->mDependencyGraph.markAsError(tb.materialInstance);
|
||||
@@ -707,7 +675,7 @@ void ResourceLoader::Impl::cancelTextureDecoding() {
|
||||
void ResourceLoader::Impl::createTextures(FFilamentAsset* asset, bool async) {
|
||||
// Create new texture objects if they are not cached and kick off decoding jobs.
|
||||
mRemainingTextureDownloads = 0;
|
||||
for (auto slot : asset->mTextureSlots) {
|
||||
for (const TextureSlot& slot : asset->mTextureSlots) {
|
||||
if (Texture* texture = getOrCreateTexture(asset, slot)) {
|
||||
asset->bindTexture(slot, texture);
|
||||
}
|
||||
@@ -744,29 +712,27 @@ void ResourceLoader::Impl::computeTangents(FFilamentAsset* asset) {
|
||||
// Create a job description for each triangle-based primitive.
|
||||
using Params = TangentsJob::Params;
|
||||
std::vector<Params> jobParams;
|
||||
for (auto pair : asset->mPrimitives) {
|
||||
if (UTILS_UNLIKELY(pair.first->type != cgltf_primitive_type_triangles)) {
|
||||
for (auto [prim, vb] : asset->mPrimitives) {
|
||||
if (UTILS_UNLIKELY(prim->type != cgltf_primitive_type_triangles)) {
|
||||
continue;
|
||||
}
|
||||
VertexBuffer* vb = pair.second;
|
||||
auto iter = baseTangents.find(vb);
|
||||
if (iter != baseTangents.end()) {
|
||||
jobParams.emplace_back(Params {{ pair.first }, {vb, nullptr, iter->second }});
|
||||
jobParams.emplace_back(Params {{ prim }, {vb, nullptr, iter->second }});
|
||||
}
|
||||
}
|
||||
|
||||
// Create a job description for morph targets.
|
||||
const NodeMap& nodeMap = asset->mInstances[0]->nodeMap;
|
||||
for (auto iter : nodeMap) {
|
||||
cgltf_node const* node = iter.first;
|
||||
cgltf_mesh const* mesh = node->mesh;
|
||||
if (UTILS_UNLIKELY(!mesh || !mesh->weights_count)) {
|
||||
for (size_t i = 0, n = asset->mSourceAsset->hierarchy->meshes_count; i < n; ++i) {
|
||||
const cgltf_mesh& mesh = asset->mSourceAsset->hierarchy->meshes[i];
|
||||
const FixedCapacityVector<Primitive>& prims = asset->mMeshCache[i];
|
||||
if (0 == mesh.weights_count) {
|
||||
continue;
|
||||
}
|
||||
for (cgltf_size pindex = 0, pcount = mesh->primitives_count; pindex < pcount; ++pindex) {
|
||||
const cgltf_primitive& prim = mesh->primitives[pindex];
|
||||
const auto& gltfioPrim = asset->mMeshCache.at(mesh)[pindex];
|
||||
MorphTargetBuffer* tb = gltfioPrim.targets;
|
||||
for (cgltf_size tindex = 0, tcount = prim.targets_count; tindex < tcount; ++ tindex) {
|
||||
for (cgltf_size pindex = 0, pcount = mesh.primitives_count; pindex < pcount; ++pindex) {
|
||||
const cgltf_primitive& prim = mesh.primitives[pindex];
|
||||
MorphTargetBuffer* tb = prims[pindex].targets;
|
||||
for (cgltf_size tindex = 0, tcount = prim.targets_count; tindex < tcount; ++tindex) {
|
||||
const cgltf_morph_target& target = prim.targets[tindex];
|
||||
bool hasNormals = false;
|
||||
for (cgltf_size aindex = 0; aindex < target.attributes_count; aindex++) {
|
||||
@@ -854,176 +820,4 @@ void ResourceLoader::normalizeSkinningWeights(FFilamentAsset* asset) const {
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceLoader::updateBoundingBoxes(FFilamentAsset* asset) const {
|
||||
SYSTRACE_CALL();
|
||||
auto& rm = pImpl->mEngine->getRenderableManager();
|
||||
auto& tm = pImpl->mEngine->getTransformManager();
|
||||
const NodeMap& nodeMap = asset->mInstances[0]->nodeMap;
|
||||
|
||||
// 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(asset->getRoot());
|
||||
std::vector<Entity> modelRoots(tm.getChildCount(root));
|
||||
tm.getChildren(root, modelRoots.data(), modelRoots.size());
|
||||
for (auto e : modelRoots) {
|
||||
tm.setParent(tm.getInstance(e), 0);
|
||||
}
|
||||
|
||||
auto computeBoundingBox = [](const cgltf_primitive* prim, Aabb* result) {
|
||||
Aabb aabb;
|
||||
for (cgltf_size slot = 0; slot < prim->attributes_count; slot++) {
|
||||
const cgltf_attribute& attr = prim->attributes[slot];
|
||||
const cgltf_accessor* accessor = attr.data;
|
||||
const size_t dim = cgltf_num_components(accessor->type);
|
||||
if (attr.type == cgltf_attribute_type_position && dim >= 3) {
|
||||
std::vector<float> unpacked(accessor->count * dim);
|
||||
cgltf_accessor_unpack_floats(accessor, unpacked.data(), unpacked.size());
|
||||
for (cgltf_size i = 0, j = 0, n = accessor->count; i < n; ++i, j += dim) {
|
||||
float3 pt(unpacked[j + 0], unpacked[j + 1], unpacked[j + 2]);
|
||||
aabb.min = min(aabb.min, pt);
|
||||
aabb.max = max(aabb.max, pt);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
*result = aabb;
|
||||
};
|
||||
|
||||
struct Prim {
|
||||
cgltf_primitive const* prim;
|
||||
Skin const* skin;
|
||||
Entity node;
|
||||
};
|
||||
|
||||
auto computeBoundingBoxSkinned = [&](const Prim& prim, Aabb* result) {
|
||||
FixedCapacityVector<float3> verts;
|
||||
FixedCapacityVector<uint4> joints;
|
||||
FixedCapacityVector<float4> weights;
|
||||
for (cgltf_size slot = 0, n = prim.prim->attributes_count; slot < n; ++slot) {
|
||||
const cgltf_attribute& attr = prim.prim->attributes[slot];
|
||||
const cgltf_accessor& accessor = *attr.data;
|
||||
switch (attr.type) {
|
||||
case cgltf_attribute_type_position:
|
||||
verts = FixedCapacityVector<float3>(accessor.count);
|
||||
cgltf_accessor_unpack_floats(&accessor, &verts.data()->x, accessor.count * 3);
|
||||
break;
|
||||
case cgltf_attribute_type_joints: {
|
||||
FixedCapacityVector<float4> tmp(accessor.count);
|
||||
cgltf_accessor_unpack_floats(&accessor, &tmp.data()->x, accessor.count * 4);
|
||||
joints = FixedCapacityVector<uint4>(accessor.count);
|
||||
for (size_t i = 0, n = accessor.count; i < n; ++i) {
|
||||
joints[i] = uint4(tmp[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cgltf_attribute_type_weights:
|
||||
weights = FixedCapacityVector<float4>(accessor.count);
|
||||
cgltf_accessor_unpack_floats(&accessor, &weights.data()->x, accessor.count * 4);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Aabb aabb;
|
||||
TransformManager::Instance transformable = tm.getInstance(prim.node);
|
||||
const mat4f inverseGlobalTransform = inverse(tm.getWorldTransform(transformable));
|
||||
for (size_t i = 0, n = verts.size(); i < n; i++) {
|
||||
float3 point = verts[i];
|
||||
mat4f tmp = mat4f(0.0f);
|
||||
for (size_t j = 0; j < 4; j++) {
|
||||
size_t jointIndex = joints[i][j];
|
||||
Entity jointEntity = prim.skin->joints[jointIndex];
|
||||
mat4f globalJointTransform = tm.getWorldTransform(tm.getInstance(jointEntity));
|
||||
mat4f inverseBindMatrix = prim.skin->inverseBindMatrices[jointIndex];
|
||||
tmp += weights[i][j] * globalJointTransform * inverseBindMatrix;
|
||||
}
|
||||
mat4f skinMatrix = inverseGlobalTransform * tmp;
|
||||
if (!pImpl->mNormalizeSkinningWeights) {
|
||||
skinMatrix /= skinMatrix[3].w;
|
||||
}
|
||||
float3 skinnedPoint = (point.x * skinMatrix[0] +
|
||||
point.y * skinMatrix[1] + point.z * skinMatrix[2] + skinMatrix[3]).xyz;
|
||||
aabb.min = min(aabb.min, skinnedPoint);
|
||||
aabb.max = max(aabb.max, skinnedPoint);
|
||||
}
|
||||
*result = aabb;
|
||||
};
|
||||
|
||||
// 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;
|
||||
for (auto iter : nodeMap) {
|
||||
const cgltf_mesh* mesh = iter.first->mesh;
|
||||
if (mesh) {
|
||||
primCount += mesh->primitives_count;
|
||||
}
|
||||
}
|
||||
auto primitives = FixedCapacityVector<Prim>::with_capacity(primCount);
|
||||
const cgltf_skin* baseSkin = &asset->mSourceAsset->hierarchy->skins[0];
|
||||
for (auto iter : nodeMap) {
|
||||
const cgltf_mesh* mesh = iter.first->mesh;
|
||||
if (mesh) {
|
||||
for (cgltf_size index = 0, nprims = mesh->primitives_count; index < nprims; ++index) {
|
||||
primitives.push_back({&mesh->primitives[index], nullptr, iter.second});
|
||||
}
|
||||
if (cgltf_skin* const skin = iter.first->skin; skin) {
|
||||
primitives.back().skin = &asset->mInstances[0]->skins[skin - baseSkin];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Kick off a bounding box job for every primitive.
|
||||
FixedCapacityVector<Aabb> bounds(primitives.size());
|
||||
JobSystem* js = &pImpl->mEngine->getJobSystem();
|
||||
JobSystem::Job* parent = js->createJob();
|
||||
for (size_t i = 0; i < primitives.size(); ++i) {
|
||||
Aabb* result = &bounds[i];
|
||||
if (pImpl->mIgnoreBindTransform || primitives[i].skin == nullptr) {
|
||||
cgltf_primitive const* prim = primitives[i].prim;
|
||||
js->run(jobs::createJob(*js, parent, [prim, result, computeBoundingBox] {
|
||||
computeBoundingBox(prim, result);
|
||||
}));
|
||||
} else {
|
||||
const Prim& prim = primitives[i];
|
||||
js->run(jobs::createJob(*js, parent, [&prim, result, computeBoundingBoxSkinned] {
|
||||
computeBoundingBoxSkinned(prim, result);
|
||||
}));
|
||||
}
|
||||
}
|
||||
js->runAndWait(parent);
|
||||
|
||||
// Compute the asset-level bounding box.
|
||||
size_t primIndex = 0;
|
||||
Aabb assetBounds;
|
||||
for (auto iter : nodeMap) {
|
||||
const cgltf_mesh* mesh = iter.first->mesh;
|
||||
if (mesh) {
|
||||
// Find the object-space bounds for the renderable by unioning the bounds of each prim.
|
||||
Aabb aabb;
|
||||
for (cgltf_size index = 0, nprims = mesh->primitives_count; index < nprims; ++index) {
|
||||
Aabb primBounds = bounds[primIndex++];
|
||||
aabb.min = min(aabb.min, primBounds.min);
|
||||
aabb.max = max(aabb.max, primBounds.max);
|
||||
}
|
||||
auto renderable = rm.getInstance(iter.second);
|
||||
rm.setAxisAlignedBoundingBox(renderable, Box().set(aabb.min, aabb.max));
|
||||
|
||||
// Transform this bounding box, then update the asset-level bounding box.
|
||||
auto transformable = tm.getInstance(iter.second);
|
||||
const mat4f worldTransform = tm.getWorldTransform(transformable);
|
||||
const Aabb transformed = aabb.transform(worldTransform);
|
||||
assetBounds.min = min(assetBounds.min, transformed.min);
|
||||
assetBounds.max = max(assetBounds.max, transformed.max);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto e : modelRoots) {
|
||||
tm.setParent(tm.getInstance(e), root);
|
||||
}
|
||||
|
||||
asset->mBoundingBox = assetBounds;
|
||||
}
|
||||
|
||||
} // namespace filament::gltfio
|
||||
|
||||
@@ -45,8 +45,8 @@ struct TangentsJob {
|
||||
// The context of the procedure. These fields are not used by the procedure but are provided as
|
||||
// a convenience to clients. You can think of this as a scratch space for clients.
|
||||
struct Context {
|
||||
filament::VertexBuffer* const vb;
|
||||
filament::MorphTargetBuffer* const tb;
|
||||
VertexBuffer* const vb;
|
||||
MorphTargetBuffer* const tb;
|
||||
const uint8_t slot;
|
||||
};
|
||||
|
||||
@@ -54,7 +54,7 @@ struct TangentsJob {
|
||||
// should remember to free it.
|
||||
struct OutputParams {
|
||||
cgltf_size vertexCount;
|
||||
filament::math::short4* results;
|
||||
math::short4* results;
|
||||
};
|
||||
|
||||
// Clients might want to track the jobs in an array, so the arguments are bundled into a struct.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user