Compare commits

...
Author SHA1 Message Date
Sungun Park 6fc16bdcda Release Filament 1.51.8 2024-05-13 20:53:36 +00:00
Mathias Agopian 4f021583f1 backend tests were broken by a change in TargetBufferInfo
- a field was added, which broke the layout of the structure. We fix it
by adding constructors which will handle the old and new way of
initializing this structure.

- one of the test needed a hash update

- OpenGLContext wrongly asserted when trying to unbind texture 0
2024-05-10 14:39:22 -07:00
Powei Feng ef15a29c0c vk: fix missing lib for backend test 2024-05-10 13:00:00 -07:00
Benjamin Doherty a0472d3c9f Rename Metal log message 2024-05-10 11:25:34 -07:00
Ben Doherty 54a800a25d Metal: implement more accurate buffer tracking (#7839) 2024-05-10 11:14:41 -07:00
Powei Feng 7f8fbe586c gl: push constant small clean-up (#7841) 2024-05-10 10:23:32 -07:00
Powei Feng 6f2c45c76d Add push constants (#7817)
- Push constants is a small set of bytes that can be recorded
   directly on the command buffer.
 - Implemented it for the vulkan/gl backend.
2024-05-09 16:14:03 -07:00
Ryan ad60008b6a ryanmyers: Improve logging for Metallib function lookup failures (#7836)
If a .metallib was compiled with a target iOS version that's newer than
the current device, loading the .metallib may succeed, but finding main0
(or any other function in it) will fail. Currently, this causes a crash
due to an assert. Logging the error and returning
MetalFunctionBundle::error() makes the crash slightly easier to
diagnose.

(Note that in practice, this will probably be a useless "Compiler
encountered an internal error" message -- the GPU backend is crashing,
and the Metal stub library sees XPC_ERROR_CONNECTION_INTERRUPTED. It
retries up to 3 times (crashing each time) and then gives up.)
2024-05-09 10:49:41 -07:00
Mathias Agopian d87f9b621b add a simple tranformmanager unit test
this was to test issue #7827
2024-05-09 09:00:41 -07:00
Ben Doherty 1b38cda0d5 Add preferredShaderLanguage Java bindings (#7835) 2024-05-08 14:42:40 -07:00
Ben Doherty a1dea7b1fa Metal: log slow buffer allocation times (#7834) 2024-05-08 14:39:53 -07:00
Mathias Agopian 744708b5ca remove the single <sstream> usage we have
the STL's stream headers can bring in a lot of code, we don't use them.
2024-05-07 16:23:31 -07:00
Benjamin Doherty e901837317 Log excess buffer allocations for Metal 2024-05-07 15:39:48 -07:00
54 changed files with 901 additions and 135 deletions
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.51.7'
implementation 'com.google.android.filament:filament-android:1.51.8'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.51.7'
pod 'Filament', '~> 1.51.8'
```
### Snapshots
+3
View File
@@ -7,6 +7,9 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.51.9
## v1.51.8
- filagui: Fix regression which broke WebGL
@@ -518,6 +518,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
jint stereoscopicType, jlong stereoscopicEyeCount,
jlong resourceAllocatorCacheSizeMB, jlong resourceAllocatorCacheMaxAge,
jboolean disableHandleUseAfterFreeCheck,
jint preferredShaderLanguage,
jboolean forceGLES2Context) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
Engine::Config config = {
@@ -534,7 +535,8 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
.resourceAllocatorCacheSizeMB = (uint32_t) resourceAllocatorCacheSizeMB,
.resourceAllocatorCacheMaxAge = (uint8_t) resourceAllocatorCacheMaxAge,
.disableHandleUseAfterFreeCheck = (bool) disableHandleUseAfterFreeCheck,
.forceGLES2Context = (bool) forceGLES2Context
.preferredShaderLanguage = (Engine::Config::ShaderLanguage) preferredShaderLanguage,
.forceGLES2Context = (bool) forceGLES2Context,
};
builder->config(&config);
}
@@ -226,6 +226,7 @@ public class Engine {
config.stereoscopicType.ordinal(), config.stereoscopicEyeCount,
config.resourceAllocatorCacheSizeMB, config.resourceAllocatorCacheMaxAge,
config.disableHandleUseAfterFreeCheck,
config.preferredShaderLanguage.ordinal(),
config.forceGLES2Context);
return this;
}
@@ -430,6 +431,28 @@ public class Engine {
*/
public boolean disableHandleUseAfterFreeCheck = false;
/*
* Sets a preferred shader language for Filament to use.
*
* The Metal backend supports two shader languages: MSL (Metal Shading Language) and
* METAL_LIBRARY (precompiled .metallib). This option controls which shader language is
* used when materials contain both.
*
* By default, when preferredShaderLanguage is unset, Filament will prefer METAL_LIBRARY
* shaders if present within a material, falling back to MSL. Setting
* preferredShaderLanguage to ShaderLanguage::MSL will instead instruct Filament to check
* for the presence of MSL in a material first, falling back to METAL_LIBRARY if MSL is not
* present.
*
* When using a non-Metal backend, setting this has no effect.
*/
public enum ShaderLanguage {
DEFAULT,
MSL,
METAL_LIBRARY,
};
public ShaderLanguage preferredShaderLanguage = ShaderLanguage.DEFAULT;
/*
* When the OpenGL ES backend is used, setting this value to true will force a GLES2.0
* context if supported by the Platform, or if not, will have the backend pretend
@@ -1362,6 +1385,7 @@ public class Engine {
int stereoscopicType, long stereoscopicEyeCount,
long resourceAllocatorCacheSizeMB, long resourceAllocatorCacheMaxAge,
boolean disableHandleUseAfterFreeCheck,
int preferredShaderLanguage,
boolean forceGLES2Context);
private static native void nSetBuilderFeatureLevel(long nativeBuilder, int ordinal);
private static native void nSetBuilderSharedContext(long nativeBuilder, long sharedContext);
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.51.7
VERSION_NAME=1.51.8
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+4
View File
@@ -478,6 +478,10 @@ if (APPLE)
# linker from removing "unused" symbols.
target_link_libraries(backend_test_mac PRIVATE -force_load backend_test)
set_target_properties(backend_test_mac PROPERTIES FOLDER Tests)
# This is needed after XCode 15.3
set_target_properties(backend_test_mac PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE)
set_target_properties(backend_test_mac PROPERTIES INSTALL_RPATH /usr/local/lib)
endif()
endif()
@@ -31,6 +31,7 @@
#include <array> // FIXME: STL headers are not allowed in public headers
#include <type_traits> // FIXME: STL headers are not allowed in public headers
#include <variant> // FIXME: STL headers are not allowed in public headers
#include <stddef.h>
#include <stdint.h>
@@ -91,12 +92,15 @@ static constexpr uint64_t SWAP_CHAIN_HAS_STENCIL_BUFFER = SWAP_CHAIN_CON
*/
static constexpr uint64_t SWAP_CHAIN_CONFIG_PROTECTED_CONTENT = 0x40;
static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_SAMPLER_COUNT = 62; // Maximum needed at feature level 3.
static constexpr size_t MAX_VERTEX_BUFFER_COUNT = 16; // Max number of bound buffer objects.
static constexpr size_t MAX_SSBO_COUNT = 4; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_PUSH_CONSTANT_COUNT = 32; // Vulkan 1.1 spec allows for 128-byte
// of push constant (we assume 4-byte
// types).
// Per feature level caps
// Use (int)FeatureLevel to index this array
static constexpr struct {
@@ -332,7 +336,7 @@ enum class UniformType : uint8_t {
/**
* Supported constant parameter types
*/
enum class ConstantType : uint8_t {
enum class ConstantType : uint8_t {
INT,
FLOAT,
BOOL
@@ -1219,6 +1223,8 @@ struct StencilState {
uint8_t padding = 0;
};
using PushConstantVariant = std::variant<int32_t, float, bool>;
static_assert(sizeof(StencilState::StencilOperations) == 5u,
"StencilOperations size not what was intended");
@@ -117,6 +117,14 @@ public:
Program& specializationConstants(
utils::FixedCapacityVector<SpecializationConstant> specConstants) noexcept;
struct PushConstant {
utils::CString name;
ConstantType type;
};
Program& pushConstants(ShaderStage stage,
utils::FixedCapacityVector<PushConstant> constants) noexcept;
Program& cacheId(uint64_t cacheId) noexcept;
Program& multiview(bool multiview) noexcept;
@@ -148,6 +156,15 @@ public:
return mSpecializationConstants;
}
utils::FixedCapacityVector<PushConstant> const& getPushConstants(
ShaderStage stage) const noexcept {
return mPushConstants[static_cast<uint8_t>(stage)];
}
utils::FixedCapacityVector<PushConstant>& getPushConstants(ShaderStage stage) noexcept {
return mPushConstants[static_cast<uint8_t>(stage)];
}
uint64_t getCacheId() const noexcept { return mCacheId; }
bool isMultiview() const noexcept { return mMultiview; }
@@ -165,6 +182,7 @@ private:
uint64_t mCacheId{};
utils::Invocable<utils::io::ostream&(utils::io::ostream& out)> mLogger;
utils::FixedCapacityVector<SpecializationConstant> mSpecializationConstants;
std::array<utils::FixedCapacityVector<PushConstant>, SHADER_TYPE_COUNT> mPushConstants;
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> mAttributes;
std::array<UniformInfo, Program::UNIFORM_BINDING_COUNT> mBindingUniformInfo;
CompilerPriorityQueue mPriorityQueue = CompilerPriorityQueue::HIGH;
@@ -29,17 +29,36 @@ namespace filament::backend {
//! \privatesection
struct TargetBufferInfo {
// note: the parameters of this constructor are not in the order of this structure's fields
TargetBufferInfo(Handle<HwTexture> handle, uint8_t level, uint16_t layer, uint8_t baseViewIndex) noexcept
: handle(handle), baseViewIndex(baseViewIndex), level(level), layer(layer) {
}
TargetBufferInfo(Handle<HwTexture> handle, uint8_t level, uint16_t layer) noexcept
: handle(handle), level(level), layer(layer) {
}
TargetBufferInfo(Handle<HwTexture> handle, uint8_t level) noexcept
: handle(handle), level(level) {
}
TargetBufferInfo(Handle<HwTexture> handle) noexcept // NOLINT(*-explicit-constructor)
: handle(handle) {
}
TargetBufferInfo() noexcept = default;
// texture to be used as render target
Handle<HwTexture> handle;
// starting layer index for multiview. This value is only used when the `layerCount` for the
// Starting layer index for multiview. This value is only used when the `layerCount` for the
// render target is greater than 1.
uint8_t baseViewIndex = 0;
// level to be used
uint8_t level = 0;
// for cubemaps and 3D textures. See TextureCubemapFace for the face->layer mapping
// For cubemaps and 3D textures. See TextureCubemapFace for the face->layer mapping
uint16_t layer = 0;
};
@@ -64,7 +83,7 @@ public:
MRT() noexcept = default;
MRT(TargetBufferInfo const& color) noexcept // NOLINT(hicpp-explicit-conversions)
MRT(TargetBufferInfo const& color) noexcept // NOLINT(hicpp-explicit-conversions, *-explicit-constructor)
: mInfos{ color } {
}
@@ -84,7 +103,7 @@ public:
// this is here for backward compatibility
MRT(Handle<HwTexture> handle, uint8_t level, uint16_t layer) noexcept
: mInfos{{ handle, 0, level, layer }} {
: mInfos{{ handle, level, layer, 0 }} {
}
};
@@ -434,6 +434,11 @@ DECL_DRIVER_API_N(bindSamplers,
uint32_t, index,
backend::SamplerGroupHandle, sbh)
DECL_DRIVER_API_N(setPushConstant,
backend::ShaderStage, stage,
uint8_t, index,
backend::PushConstantVariant, value)
DECL_DRIVER_API_N(insertEventMarker,
const char*, string,
uint32_t, len = 0)
+6
View File
@@ -91,6 +91,12 @@ Program& Program::specializationConstants(
return *this;
}
Program& Program::pushConstants(ShaderStage stage,
utils::FixedCapacityVector<PushConstant> constants) noexcept {
mPushConstants[static_cast<uint8_t>(stage)] = std::move(constants);
return *this;
}
Program& Program::cacheId(uint64_t cacheId) noexcept {
mCacheId = cacheId;
return *this;
+92 -48
View File
@@ -18,6 +18,7 @@
#define TNT_FILAMENT_DRIVER_METALBUFFER_H
#include "MetalContext.h"
#include "MetalPlatform.h"
#include <backend/DriverEnums.h>
@@ -28,11 +29,49 @@
#include <utility>
#include <memory>
#include <atomic>
#include <chrono>
namespace filament::backend {
class TrackedMetalBuffer {
class ScopedAllocationTimer {
public:
ScopedAllocationTimer(const char* name) : mBeginning(clock_t::now()), mName(name) {}
~ScopedAllocationTimer() {
using namespace std::literals::chrono_literals;
static constexpr std::chrono::seconds LONG_TIME_THRESHOLD = 10s;
auto end = clock_t::now();
std::chrono::duration<double, std::micro> allocationTimeMicroseconds = end - mBeginning;
if (UTILS_UNLIKELY(allocationTimeMicroseconds > LONG_TIME_THRESHOLD)) {
if (platform && platform->hasDebugUpdateStatFunc()) {
char buffer[64];
snprintf(buffer, sizeof(buffer), "filament.metal.long_buffer_allocation_time.%s",
mName);
platform->debugUpdateStat(
buffer, static_cast<uint64_t>(allocationTimeMicroseconds.count()));
}
}
}
static void setPlatform(MetalPlatform* p) { platform = p; }
private:
typedef std::chrono::steady_clock clock_t;
static MetalPlatform* platform;
std::chrono::time_point<clock_t> mBeginning;
const char* mName;
};
#ifndef FILAMENT_METAL_BUFFER_TRACKING
#define FILAMENT_METAL_BUFFER_TRACKING 0
#endif
class MetalBufferTracking {
public:
static constexpr size_t EXCESS_BUFFER_COUNT = 30000;
enum class Type {
NONE = 0,
@@ -55,58 +94,57 @@ public:
}
}
TrackedMetalBuffer() noexcept : mBuffer(nil) {}
TrackedMetalBuffer(nullptr_t) noexcept : mBuffer(nil) {}
TrackedMetalBuffer(id<MTLBuffer> buffer, Type type) : mBuffer(buffer), mType(type) {
#if FILAMENT_METAL_BUFFER_TRACKING
static void initialize() {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
for (size_t i = 0; i < TypeCount; i++) {
aliveBuffers[i] = [NSHashTable weakObjectsHashTable];
}
});
}
static void setPlatform(MetalPlatform* p) { platform = p; }
static void track(id<MTLBuffer> buffer, Type type) {
assert_invariant(type != Type::NONE);
if (buffer) {
aliveBuffers[toIndex(type)]++;
mType = type;
if (UTILS_UNLIKELY(getAliveBuffers() >= EXCESS_BUFFER_COUNT)) {
if (platform && platform->hasDebugUpdateStatFunc()) {
platform->debugUpdateStat("filament.metal.excess_buffers_allocated",
MetalBufferTracking::getAliveBuffers());
}
}
[aliveBuffers[toIndex(type)] addObject:buffer];
}
~TrackedMetalBuffer() {
if (mBuffer) {
assert_invariant(mType != Type::NONE);
aliveBuffers[toIndex(mType)]--;
}
}
TrackedMetalBuffer(TrackedMetalBuffer&&) = delete;
TrackedMetalBuffer(TrackedMetalBuffer const&) = delete;
TrackedMetalBuffer& operator=(TrackedMetalBuffer const&) = delete;
TrackedMetalBuffer& operator=(TrackedMetalBuffer&& rhs) noexcept {
swap(rhs);
return *this;
}
id<MTLBuffer> get() const noexcept { return mBuffer; }
operator bool() const noexcept { return bool(mBuffer); }
static uint64_t getAliveBuffers() {
uint64_t sum = 0;
for (const auto& v : aliveBuffers) {
sum += v;
for (size_t i = 1; i < TypeCount; i++) {
sum += getAliveBuffers(static_cast<Type>(i));
}
return sum;
}
static uint64_t getAliveBuffers(Type type) {
assert_invariant(type != Type::NONE);
return aliveBuffers[toIndex(type)];
NSHashTable* hashTable = aliveBuffers[toIndex(type)];
// Caution! We can't simply use hashTable.count here, which is inaccurate.
// See http://cocoamine.net/blog/2013/12/13/nsmaptable-and-zeroing-weak-references/
return hashTable.objectEnumerator.allObjects.count;
}
#else
static void initialize() {}
static void setPlatform(MetalPlatform* p) {}
static id<MTLBuffer> track(id<MTLBuffer> buffer, Type type) { return buffer; }
static uint64_t getAliveBuffers() { return 0; }
static uint64_t getAliveBuffers(Type type) { return 0; }
#endif
private:
void swap(TrackedMetalBuffer& other) noexcept {
std::swap(mBuffer, other.mBuffer);
std::swap(mType, other.mType);
}
id<MTLBuffer> mBuffer;
Type mType = Type::NONE;
static std::array<uint64_t, TypeCount> aliveBuffers;
#if FILAMENT_METAL_BUFFER_TRACKING
static std::array<NSHashTable<id<MTLBuffer>>*, TypeCount> aliveBuffers;
static MetalPlatform* platform;
#endif
};
class MetalBuffer {
@@ -160,7 +198,7 @@ public:
private:
TrackedMetalBuffer mBuffer;
id<MTLBuffer> mBuffer;
size_t mBufferSize = 0;
void* mCpuBuffer = nullptr;
MetalContext& mContext;
@@ -209,8 +247,11 @@ public:
mBufferOptions(options),
mSlotSizeBytes(computeSlotSize(layout)),
mSlotCount(slotCount) {
mBuffer = { [device newBufferWithLength:mSlotSizeBytes * mSlotCount options:mBufferOptions],
TrackedMetalBuffer::Type::RING };
{
ScopedAllocationTimer timer("ring");
mBuffer = [device newBufferWithLength:mSlotSizeBytes * mSlotCount options:mBufferOptions];
}
MetalBufferTracking::track(mBuffer, MetalBufferTracking::Type::RING);
assert_invariant(mBuffer);
}
@@ -228,10 +269,13 @@ public:
// If we already have an aux buffer, it will get freed here, unless it has been retained
// by a MTLCommandBuffer. In that case, it will be freed when the command buffer
// finishes executing.
mAuxBuffer = { [mDevice newBufferWithLength:mSlotSizeBytes options:mBufferOptions],
TrackedMetalBuffer::Type::RING };
{
ScopedAllocationTimer timer("ring");
mAuxBuffer = [mDevice newBufferWithLength:mSlotSizeBytes options:mBufferOptions];
}
MetalBufferTracking::track(mAuxBuffer, MetalBufferTracking::Type::RING);
assert_invariant(mAuxBuffer);
return { mAuxBuffer.get(), 0 };
return { mAuxBuffer, 0 };
}
mCurrentSlot = (mCurrentSlot + 1) % mSlotCount;
mOccupiedSlots->fetch_add(1, std::memory_order_relaxed);
@@ -260,9 +304,9 @@ public:
*/
std::pair<id<MTLBuffer>, NSUInteger> getCurrentAllocation() const {
if (UTILS_UNLIKELY(mAuxBuffer)) {
return { mAuxBuffer.get(), 0 };
return { mAuxBuffer, 0 };
}
return { mBuffer.get(), mCurrentSlot * mSlotSizeBytes };
return { mBuffer, mCurrentSlot * mSlotSizeBytes };
}
bool canAccomodateLayout(MTLSizeAndAlign layout) const {
@@ -271,8 +315,8 @@ public:
private:
id<MTLDevice> mDevice;
TrackedMetalBuffer mBuffer;
TrackedMetalBuffer mAuxBuffer;
id<MTLBuffer> mBuffer;
id<MTLBuffer> mAuxBuffer;
MTLResourceOptions mBufferOptions;
+16 -7
View File
@@ -22,7 +22,13 @@
namespace filament {
namespace backend {
std::array<uint64_t, TrackedMetalBuffer::TypeCount> TrackedMetalBuffer::aliveBuffers = { 0 };
MetalPlatform* ScopedAllocationTimer::platform = nullptr;
#if FILAMENT_METAL_BUFFER_TRACKING
std::array<NSHashTable<id<MTLBuffer>>*, MetalBufferTracking::TypeCount>
MetalBufferTracking::aliveBuffers;
MetalPlatform* MetalBufferTracking::platform = nullptr;
#endif
MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType, BufferUsage usage,
size_t size, bool forceGpuBuffer) : mBufferSize(size), mContext(context) {
@@ -37,8 +43,11 @@ MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType,
}
// Otherwise, we allocate a private GPU buffer.
mBuffer = { [context.device newBufferWithLength:size options:MTLResourceStorageModePrivate],
TrackedMetalBuffer::Type::GENERIC };
{
ScopedAllocationTimer timer("generic");
mBuffer = [context.device newBufferWithLength:size options:MTLResourceStorageModePrivate];
}
MetalBufferTracking::track(mBuffer, MetalBufferTracking::Type::GENERIC);
ASSERT_POSTCONDITION(mBuffer, "Could not allocate Metal buffer of size %zu.", size);
}
@@ -65,7 +74,7 @@ void MetalBuffer::copyIntoBuffer(void* src, size_t size, size_t byteOffset) {
// Acquire a staging buffer to hold the contents of this update.
MetalBufferPool* bufferPool = mContext.bufferPool;
const MetalBufferPoolEntry* const staging = bufferPool->acquireBuffer(size);
memcpy(staging->buffer.get().contents, src, size);
memcpy(staging->buffer.contents, src, size);
// The blit below requires that byteOffset be a multiple of 4.
ASSERT_PRECONDITION(!(byteOffset & 0x3u), "byteOffset must be a multiple of 4");
@@ -74,9 +83,9 @@ void MetalBuffer::copyIntoBuffer(void* src, size_t size, size_t byteOffset) {
id<MTLCommandBuffer> cmdBuffer = getPendingCommandBuffer(&mContext);
id<MTLBlitCommandEncoder> blitEncoder = [cmdBuffer blitCommandEncoder];
blitEncoder.label = @"Buffer upload blit";
[blitEncoder copyFromBuffer:staging->buffer.get()
[blitEncoder copyFromBuffer:staging->buffer
sourceOffset:0
toBuffer:mBuffer.get()
toBuffer:mBuffer
destinationOffset:byteOffset
size:size];
[blitEncoder endEncoding];
@@ -97,7 +106,7 @@ id<MTLBuffer> MetalBuffer::getGpuBufferForDraw(id<MTLCommandBuffer> cmdBuffer) n
return nil;
}
assert_invariant(mBuffer);
return mBuffer.get();
return mBuffer;
}
void MetalBuffer::bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLCommandEncoder> encoder,
+1 -1
View File
@@ -32,7 +32,7 @@ struct MetalContext;
// Immutable POD representing a shared CPU-GPU buffer.
struct MetalBufferPoolEntry {
TrackedMetalBuffer buffer;
id<MTLBuffer> buffer;
size_t capacity;
mutable uint64_t lastAccessed;
mutable uint32_t referenceCount;
@@ -42,11 +42,16 @@ MetalBufferPoolEntry const* MetalBufferPool::acquireBuffer(size_t numBytes) {
}
// We were not able to find a sufficiently large stage, so create a new one.
id<MTLBuffer> buffer = [mContext.device newBufferWithLength:numBytes
options:MTLResourceStorageModeShared];
id<MTLBuffer> buffer = nil;
{
ScopedAllocationTimer timer("staging");
buffer = [mContext.device newBufferWithLength:numBytes
options:MTLResourceStorageModeShared];
}
MetalBufferTracking::track(buffer, MetalBufferTracking::Type::STAGING);
ASSERT_POSTCONDITION(buffer, "Could not allocate Metal staging buffer of size %zu.", numBytes);
MetalBufferPoolEntry* stage = new MetalBufferPoolEntry {
.buffer = { buffer, TrackedMetalBuffer::Type::STAGING },
.buffer = buffer,
.capacity = numBytes,
.lastAccessed = mCurrentFrame,
.referenceCount = 1
+19 -7
View File
@@ -105,6 +105,10 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
driverConfig.disableHandleUseAfterFreeCheck) {
mContext->driver = this;
ScopedAllocationTimer::setPlatform(platform);
MetalBufferTracking::initialize();
MetalBufferTracking::setPlatform(platform);
mContext->device = mPlatform.createDevice();
assert_invariant(mContext->device);
@@ -198,6 +202,8 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
}
MetalDriver::~MetalDriver() noexcept {
MetalBufferTracking::setPlatform(nullptr);
ScopedAllocationTimer::setPlatform(nullptr);
mContext->device = nil;
mContext->emptyTexture = nil;
CFRelease(mContext->textureCache);
@@ -218,13 +224,16 @@ void MetalDriver::beginFrame(int64_t monotonic_clock_ns,
os_signpost_interval_begin(mContext->log, mContext->signpostId, "Frame encoding", "%{public}d", frameId);
#endif
if (mPlatform.hasDebugUpdateStatFunc()) {
mPlatform.debugUpdateStat("filament.metal.alive_buffers", TrackedMetalBuffer::getAliveBuffers());
mPlatform.debugUpdateStat("filament.metal.alive_buffers.generic",
TrackedMetalBuffer::getAliveBuffers(TrackedMetalBuffer::Type::GENERIC));
mPlatform.debugUpdateStat("filament.metal.alive_buffers.ring",
TrackedMetalBuffer::getAliveBuffers(TrackedMetalBuffer::Type::RING));
mPlatform.debugUpdateStat("filament.metal.alive_buffers.staging",
TrackedMetalBuffer::getAliveBuffers(TrackedMetalBuffer::Type::STAGING));
#if FILAMENT_METAL_BUFFER_TRACKING
const uint64_t generic = MetalBufferTracking::getAliveBuffers(MetalBufferTracking::Type::GENERIC);
const uint64_t ring = MetalBufferTracking::getAliveBuffers(MetalBufferTracking::Type::RING);
const uint64_t staging = MetalBufferTracking::getAliveBuffers(MetalBufferTracking::Type::STAGING);
const uint64_t total = generic + ring + staging;
mPlatform.debugUpdateStat("filament.metal.alive_buffers", total);
mPlatform.debugUpdateStat("filament.metal.alive_buffers.generic", generic);
mPlatform.debugUpdateStat("filament.metal.alive_buffers.ring", ring);
mPlatform.debugUpdateStat("filament.metal.alive_buffers.staging", staging);
#endif
}
}
@@ -1247,6 +1256,9 @@ void MetalDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
mContext->samplerBindings[index] = sb;
}
void MetalDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
backend::PushConstantVariant value) {}
void MetalDriver::insertEventMarker(const char* string, uint32_t len) {
}
+2 -2
View File
@@ -789,13 +789,13 @@ void MetalTexture::loadWithCopyBuffer(uint32_t level, uint32_t slice, MTLRegion
PixelBufferDescriptor const& data, const PixelBufferShape& shape) {
const size_t stagingBufferSize = shape.totalBytes;
auto entry = context.bufferPool->acquireBuffer(stagingBufferSize);
memcpy(entry->buffer.get().contents,
memcpy(entry->buffer.contents,
static_cast<uint8_t*>(data.buffer) + shape.sourceOffset,
stagingBufferSize);
id<MTLCommandBuffer> blitCommandBuffer = getPendingCommandBuffer(&context);
id<MTLBlitCommandEncoder> blitCommandEncoder = [blitCommandBuffer blitCommandEncoder];
blitCommandEncoder.label = @"Texture upload buffer blit";
[blitCommandEncoder copyFromBuffer:entry->buffer.get()
[blitCommandEncoder copyFromBuffer:entry->buffer
sourceOffset:0
sourceBytesPerRow:shape.bytesPerRow
sourceBytesPerImage:shape.bytesPerSlice
@@ -169,6 +169,23 @@ bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
id<MTLFunction> function = [library newFunctionWithName:@"main0"
constantValues:constants
error:&error];
if (function == nil) {
// If the library loads but functions within it fail to load, it usually means the
// GPU backend crashed. (This can happen if it's a Metallib shader that was compiled
// with a minimum iOS version that's newer than this device.)
NSString* errorMessage = @"unknown error";
if (error) {
auto description =
[error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding];
utils::slog.w << description << utils::io::endl;
errorMessage = error.localizedDescription;
}
PANIC_LOG("Failed to load main0 in Metal program.");
NSString* programName =
[NSString stringWithFormat:@"%s::main0", program.getName().c_str_safe()];
return MetalFunctionBundle::error(errorMessage, programName);
}
if (!program.getName().empty()) {
function.label = @(program.getName().c_str());
}
+4
View File
@@ -312,6 +312,10 @@ void NoopDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index) {
void NoopDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
}
void NoopDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
backend::PushConstantVariant value) {
}
void NoopDriver::insertEventMarker(char const* string, uint32_t len) {
}
+10 -6
View File
@@ -894,12 +894,16 @@ void OpenGLContext::unbindTexture(
// unbind this texture from all the units it might be bound to
// no need unbind the texture from FBOs because we're not tracking that state (and there is
// no need to).
UTILS_NOUNROLL
for (GLuint unit = 0; unit < MAX_TEXTURE_UNIT_COUNT; unit++) {
if (state.textures.units[unit].id == texture_id) {
// if this texture is bound, it should be at the same target
assert_invariant(state.textures.units[unit].target == target);
unbindTextureUnit(unit);
// Never attempt to unbind texture 0. This could happen with external textures w/ streaming if
// never populated.
if (texture_id) {
UTILS_NOUNROLL
for (GLuint unit = 0; unit < MAX_TEXTURE_UNIT_COUNT; unit++) {
if (state.textures.units[unit].id == texture_id) {
// if this texture is bound, it should be at the same target
assert_invariant(state.textures.units[unit].target == target);
unbindTextureUnit(unit);
}
}
}
}
+39 -1
View File
@@ -212,7 +212,8 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfi
mHandleAllocator("Handles",
driverConfig.handleArenaSize,
driverConfig.disableHandleUseAfterFreeCheck),
mDriverConfig(driverConfig) {
mDriverConfig(driverConfig),
mCurrentPushConstants(new(std::nothrow) PushConstantBundle{}) {
std::fill(mSamplerBindings.begin(), mSamplerBindings.end(), nullptr);
@@ -269,6 +270,9 @@ void OpenGLDriver::terminate() {
assert_invariant(mGpuCommandCompleteOps.empty());
#endif
delete mCurrentPushConstants;
mCurrentPushConstants = nullptr;
mContext.terminate();
mPlatform.terminate();
@@ -289,6 +293,39 @@ void OpenGLDriver::bindSampler(GLuint unit, GLuint sampler) noexcept {
mContext.bindSampler(unit, sampler);
}
void OpenGLDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
backend::PushConstantVariant value) {
assert_invariant(stage == ShaderStage::VERTEX || stage == ShaderStage::FRAGMENT);
utils::Slice<std::pair<GLint, ConstantType>> constants;
if (stage == ShaderStage::VERTEX) {
constants = mCurrentPushConstants->vertexConstants;
} else if (stage == ShaderStage::FRAGMENT) {
constants = mCurrentPushConstants->fragmentConstants;
}
assert_invariant(index < constants.size());
auto const& [location, type] = constants[index];
// This push constant wasn't found in the shader. It's ok to return without error-ing here.
if (location < 0) {
return;
}
if (std::holds_alternative<bool>(value)) {
assert_invariant(type == ConstantType::BOOL);
bool const bval = std::get<bool>(value);
glUniform1i(location, bval ? 1 : 0);
} else if (std::holds_alternative<float>(value)) {
assert_invariant(type == ConstantType::FLOAT);
float const fval = std::get<float>(value);
glUniform1f(location, fval);
} else {
assert_invariant(type == ConstantType::INT);
int const ival = std::get<int>(value);
glUniform1i(location, ival);
}
}
void OpenGLDriver::bindTexture(GLuint unit, GLTexture const* t) noexcept {
assert_invariant(t != nullptr);
mContext.bindTexture(unit, t->gl.target, t->gl.id);
@@ -3808,6 +3845,7 @@ void OpenGLDriver::bindPipeline(PipelineState state) {
gl.polygonOffset(state.polygonOffset.slope, state.polygonOffset.constant);
OpenGLProgram* const p = handle_cast<OpenGLProgram*>(state.program);
mValidProgram = useProgram(p);
(*mCurrentPushConstants) = p->getPushConstants();
}
void OpenGLDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
+3 -1
View File
@@ -66,9 +66,9 @@ namespace filament::backend {
class OpenGLPlatform;
class PixelBufferDescriptor;
struct TargetBufferInfo;
class OpenGLProgram;
class TimerQueryFactoryInterface;
struct PushConstantBundle;
class OpenGLDriver final : public DriverBase {
inline explicit OpenGLDriver(OpenGLPlatform* platform,
@@ -375,6 +375,8 @@ private:
// for ES2 sRGB support
GLSwapChain* mCurrentDrawSwapChain = nullptr;
bool mRec709OutputColorspace = false;
PushConstantBundle* mCurrentPushConstants = nullptr;
};
// ------------------------------------------------------------------------------------------------
+19 -1
View File
@@ -46,6 +46,8 @@ struct OpenGLProgram::LazyInitializationData {
Program::UniformBlockInfo uniformBlockInfo;
Program::SamplerGroupInfo samplerGroupInfo;
std::array<Program::UniformInfo, Program::UNIFORM_BINDING_COUNT> bindingUniformInfo;
utils::FixedCapacityVector<Program::PushConstant> vertexPushConstants;
utils::FixedCapacityVector<Program::PushConstant> fragmentPushConstants;
};
@@ -53,7 +55,6 @@ OpenGLProgram::OpenGLProgram() noexcept = default;
OpenGLProgram::OpenGLProgram(OpenGLDriver& gld, Program&& program) noexcept
: HwProgram(std::move(program.getName())) {
auto* const lazyInitializationData = new(std::nothrow) LazyInitializationData();
lazyInitializationData->samplerGroupInfo = std::move(program.getSamplerGroupInfo());
if (UTILS_UNLIKELY(gld.getContext().isES2())) {
@@ -61,6 +62,8 @@ OpenGLProgram::OpenGLProgram(OpenGLDriver& gld, Program&& program) noexcept
} else {
lazyInitializationData->uniformBlockInfo = std::move(program.getUniformBlockBindings());
}
lazyInitializationData->vertexPushConstants = std::move(program.getPushConstants(ShaderStage::VERTEX));
lazyInitializationData->fragmentPushConstants = std::move(program.getPushConstants(ShaderStage::FRAGMENT));
ShaderCompilerService& compiler = gld.getShaderCompilerService();
mToken = compiler.createProgram(name, std::move(program));
@@ -203,6 +206,21 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
}
}
mUsedBindingsCount = usedBindingCount;
auto& vertexConstants = lazyInitializationData.vertexPushConstants;
auto& fragmentConstants = lazyInitializationData.fragmentPushConstants;
size_t const totalConstantCount = vertexConstants.size() + fragmentConstants.size();
if (totalConstantCount > 0) {
mPushConstants.reserve(totalConstantCount);
mPushConstantFragmentStageOffset = vertexConstants.size();
auto const transformAndAdd = [&](Program::PushConstant const& constant) {
GLint const loc = glGetUniformLocation(program, constant.name.c_str());
mPushConstants.push_back({loc, constant.type});
};
std::for_each(vertexConstants.cbegin(), vertexConstants.cend(), transformAndAdd);
std::for_each(fragmentConstants.cbegin(), fragmentConstants.cend(), transformAndAdd);
}
}
void OpenGLProgram::updateSamplers(OpenGLDriver* const gld) const noexcept {
+25 -4
View File
@@ -27,6 +27,7 @@
#include <utils/compiler.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Slice.h>
#include <array>
#include <limits>
@@ -38,6 +39,11 @@ namespace filament::backend {
class OpenGLDriver;
struct PushConstantBundle {
utils::Slice<std::pair<GLint, ConstantType>> vertexConstants;
utils::Slice<std::pair<GLint, ConstantType>> fragmentConstants;
};
class OpenGLProgram : public HwProgram {
public:
@@ -78,6 +84,14 @@ public:
GLuint program = 0;
} gl; // 4 bytes
PushConstantBundle getPushConstants() {
auto fragBegin = mPushConstants.begin() + mPushConstantFragmentStageOffset;
return {
.vertexConstants = utils::Slice(mPushConstants.begin(), fragBegin),
.fragmentConstants = utils::Slice(fragBegin, mPushConstants.end()),
};
}
private:
// keep these away from of other class attributes
struct LazyInitializationData;
@@ -95,11 +109,14 @@ private:
ShaderCompilerService::program_token_t mToken{}; // 16 bytes
uint8_t mUsedBindingsCount = 0u; // 1 byte
UTILS_UNUSED uint8_t padding[3] = {}; // 3 bytes
UTILS_UNUSED uint8_t padding[2] = {}; // 2 byte
// Push constant array offset for fragment stage constants.
uint8_t mPushConstantFragmentStageOffset = 0u; // 1 byte
// only needed for ES2
GLint mRec709Location = -1; // 4 bytes
GLint mRec709Location = -1; // 4 bytes
using LocationInfo = utils::FixedCapacityVector<GLint>;
struct UniformsRecord {
Program::UniformInfo uniforms;
@@ -107,11 +124,15 @@ private:
mutable GLuint id = 0;
mutable uint16_t age = std::numeric_limits<uint16_t>::max();
};
UniformsRecord const* mUniformsRecords = nullptr;
UniformsRecord const* mUniformsRecords = nullptr; // 8 bytes
// Note that this can be replaced with a raw pointer and an uint8_t (for size) to reduce the
// size of the container to 9 bytes if there is a need in the future.
utils::FixedCapacityVector<std::pair<GLint, ConstantType>> mPushConstants;// 16 bytes
};
// if OpenGLProgram is larger tha 64 bytes, it'll fall in a larger Handle bucket.
static_assert(sizeof(OpenGLProgram) <= 64); // currently 48 bytes
static_assert(sizeof(OpenGLProgram) <= 64); // currently 64 bytes
} // namespace filament::backend
+1
View File
@@ -410,6 +410,7 @@ io::ostream& operator<<(io::ostream& out, const RasterState& rs) {
io::ostream& operator<<(io::ostream& out, const TargetBufferInfo& tbi) {
return out << "TargetBufferInfo{"
<< "handle=" << tbi.handle
<< ", baseViewIndex=" << tbi.baseViewIndex
<< ", level=" << tbi.level
<< ", layer=" << tbi.layer << "}";
}
+17 -3
View File
@@ -259,8 +259,8 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
mDescriptorSetManager.setPlaceHolders(mSamplerCache.getSampler({}), mEmptyTexture,
mEmptyBufferObject);
mGetPipelineFunction = [this](VulkanDescriptorSetLayoutList const& layouts) {
return mPipelineLayoutCache.getLayout(layouts);
mGetPipelineFunction = [this](VulkanDescriptorSetLayoutList const& layouts, VulkanProgram* program) {
return mPipelineLayoutCache.getLayout(layouts, program);
};
}
@@ -1572,6 +1572,14 @@ void VulkanDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
mSamplerBindings[index] = hwsb;
}
void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
backend::PushConstantVariant value) {
assert_invariant(mBoundPipeline.program && "Expect a program when writing to push constants");
VulkanCommands* commands = &mCommands;
mBoundPipeline.program->writePushConstant(commands, mBoundPipeline.pipelineLayout, stage, index,
value);
}
void VulkanDriver::insertEventMarker(char const* string, uint32_t len) {
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
mCommands.insertEventMarker(string, len);
@@ -1857,7 +1865,13 @@ void VulkanDriver::bindPipeline(PipelineState pipelineState) {
mDescriptorSetManager.updateSampler({}, binding, texture, vksampler);
}
mPipelineCache.bindLayout(mDescriptorSetManager.bind(commands, program, mGetPipelineFunction));
auto const pipelineLayout = mDescriptorSetManager.bind(commands, program, mGetPipelineFunction);
mBoundPipeline = {
.program = program,
.pipelineLayout = pipelineLayout,
};
mPipelineCache.bindLayout(pipelineLayout);
mPipelineCache.bindPipeline(commands);
FVK_SYSTRACE_END();
}
@@ -160,6 +160,13 @@ private:
VulkanDescriptorSetManager::GetPipelineLayoutFunction mGetPipelineFunction;
// This is necessary for us to write to push constants after binding a pipeline.
struct BoundPipeline {
VulkanProgram* program;
VkPipelineLayout pipelineLayout;
};
BoundPipeline mBoundPipeline = {};
RenderPassFboBundle mRenderPassFboInfo;
bool const mIsSRGBSwapChainSupported;
+60 -5
View File
@@ -112,27 +112,82 @@ inline VkDescriptorSetLayout createDescriptorSetLayout(VkDevice device,
return layout;
}
inline VkShaderStageFlags getVkStage(backend::ShaderStage stage) {
switch(stage) {
case backend::ShaderStage::VERTEX:
return VK_SHADER_STAGE_VERTEX_BIT;
case backend::ShaderStage::FRAGMENT:
return VK_SHADER_STAGE_FRAGMENT_BIT;
case backend::ShaderStage::COMPUTE:
PANIC_POSTCONDITION("Unsupported stage");
}
}
} // anonymous namespace
VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(VkDevice device, VkDescriptorSetLayoutCreateInfo const& info,
Bitmask const& bitmask)
VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(VkDevice device,
VkDescriptorSetLayoutCreateInfo const& info, Bitmask const& bitmask)
: VulkanResource(VulkanResourceType::DESCRIPTOR_SET_LAYOUT),
mDevice(device),
vklayout(createDescriptorSetLayout(device, info)),
bitmask(bitmask),
bindings(getBindings(bitmask)),
count(Count::fromLayoutBitmask(bitmask)) {
}
count(Count::fromLayoutBitmask(bitmask)) {}
VulkanDescriptorSetLayout::~VulkanDescriptorSetLayout() {
vkDestroyDescriptorSetLayout(mDevice, vklayout, VKALLOC);
}
PushConstantDescription::PushConstantDescription(backend::Program const& program) noexcept {
mRangeCount = 0;
for (auto stage : { ShaderStage::VERTEX, ShaderStage::FRAGMENT, ShaderStage::COMPUTE }) {
auto const& constants = program.getPushConstants(stage);
if (constants.empty()) {
continue;
}
// We store the type of the constant for type-checking when writing.
auto& types = mTypes[(uint8_t) stage];
types.reserve(constants.size());
std::for_each(constants.cbegin(), constants.cend(), [&types] (Program::PushConstant t) {
types.push_back(t.type);
});
mRanges[mRangeCount++] = {
.stageFlags = getVkStage(stage),
.offset = 0,
.size = (uint32_t) constants.size() * ENTRY_SIZE,
};
}
}
void PushConstantDescription::write(VulkanCommands* commands, VkPipelineLayout layout,
backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value) {
VulkanCommandBuffer* cmdbuf = &(commands->get());
uint32_t binaryValue = 0;
UTILS_UNUSED_IN_RELEASE auto const& types = mTypes[(uint8_t) stage];
if (std::holds_alternative<bool>(value)) {
assert_invariant(types[index] == ConstantType::BOOL);
bool const bval = std::get<bool>(value);
binaryValue = static_cast<uint32_t const>(bval ? VK_TRUE : VK_FALSE);
} else if (std::holds_alternative<float>(value)) {
assert_invariant(types[index] == ConstantType::FLOAT);
float const fval = std::get<float>(value);
binaryValue = *reinterpret_cast<uint32_t const*>(&fval);
} else {
assert_invariant(types[index] == ConstantType::INT);
int const ival = std::get<int>(value);
binaryValue = *reinterpret_cast<uint32_t const*>(&ival);
}
vkCmdPushConstants(cmdbuf->buffer(), layout, getVkStage(stage), index * ENTRY_SIZE, ENTRY_SIZE,
&binaryValue);
}
VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
: HwProgram(builder.getName()),
VulkanResource(VulkanResourceType::PROGRAM),
mInfo(new PipelineInfo()),
mInfo(new(std::nothrow) PipelineInfo(builder)),
mDevice(device) {
constexpr uint8_t UBO_MODULE_OFFSET = (sizeof(UniformBufferBitmask) * 8) / MAX_SHADER_MODULES;
+43 -5
View File
@@ -26,10 +26,10 @@
#include "VulkanTexture.h"
#include "VulkanUtility.h"
#include "private/backend/SamplerGroup.h"
#include "utils/FixedCapacityVector.h"
#include "vulkan/vulkan_core.h"
#include <private/backend/SamplerGroup.h>
#include <backend/Program.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Mutex.h>
#include <utils/StructureOfArrays.h>
@@ -180,6 +180,28 @@ private:
using VulkanDescriptorSetList = std::array<Handle<VulkanDescriptorSet>,
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
using PushConstantNameArray = utils::FixedCapacityVector<char const*>;
using PushConstantNameByStage = std::array<PushConstantNameArray, Program::SHADER_TYPE_COUNT>;
struct PushConstantDescription {
explicit PushConstantDescription(backend::Program const& program) noexcept;
VkPushConstantRange const* getVkRanges() const noexcept { return mRanges; }
uint32_t getVkRangeCount() const noexcept { return mRangeCount; }
void write(VulkanCommands* commands, VkPipelineLayout layout, backend::ShaderStage stage,
uint8_t index, backend::PushConstantVariant const& value);
private:
static constexpr uint32_t ENTRY_SIZE = sizeof(uint32_t);
utils::FixedCapacityVector<backend::ConstantType> mTypes[Program::SHADER_TYPE_COUNT];
VkPushConstantRange mRanges[Program::SHADER_TYPE_COUNT];
uint32_t mRangeCount;
};
struct VulkanProgram : public HwProgram, VulkanResource {
using BindingList = CappedArray<uint16_t, MAX_SAMPLER_COUNT>;
@@ -212,6 +234,19 @@ struct VulkanProgram : public HwProgram, VulkanResource {
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
inline LayoutDescriptionList const& getLayoutDescriptionList() const { return mInfo->layouts; }
inline uint32_t getPushConstantRangeCount() const {
return mInfo->pushConstantDescription.getVkRangeCount();
}
inline VkPushConstantRange const* getPushConstantRanges() const {
return mInfo->pushConstantDescription.getVkRanges();
}
inline void writePushConstant(VulkanCommands* commands, VkPipelineLayout layout,
backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value) {
mInfo->pushConstantDescription.write(commands, layout, stage, index, value);
}
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
inline utils::FixedCapacityVector<std::string> const& getBindingToName() const {
return mInfo->bindingToName;
@@ -224,8 +259,9 @@ struct VulkanProgram : public HwProgram, VulkanResource {
private:
struct PipelineInfo {
PipelineInfo()
: bindingToSamplerIndex(MAX_SAMPLER_COUNT, 0xffff)
explicit PipelineInfo(backend::Program const& program) noexcept
: bindingToSamplerIndex(MAX_SAMPLER_COUNT, 0xffff),
pushConstantDescription(program)
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
, bindingToName(MAX_SAMPLER_COUNT, "")
#endif
@@ -241,6 +277,8 @@ private:
// descset::DescriptorSetLayout layout;
LayoutDescriptionList layouts;
PushConstantDescription pushConstantDescription;
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
// We store the sampler name mapped from binding index (only for debug purposes).
utils::FixedCapacityVector<std::string> bindingToName;
@@ -879,7 +879,7 @@ public:
vkUpdateDescriptorSets(mDevice, nwrites, descriptorWrites, 0, nullptr);
}
VkPipelineLayout const pipelineLayout = getPipelineLayoutFn(outLayouts);
VkPipelineLayout const pipelineLayout = getPipelineLayoutFn(outLayouts, program);
VkCommandBuffer const cmdbuffer = commands->buffer();
BoundState state{};
@@ -45,8 +45,8 @@ class VulkanDescriptorSetManager {
public:
static constexpr uint8_t UNIQUE_DESCRIPTOR_SET_COUNT =
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT;
using GetPipelineLayoutFunction =
std::function<VkPipelineLayout(VulkanDescriptorSetLayoutList const&)>;
using GetPipelineLayoutFunction = std::function<VkPipelineLayout(
VulkanDescriptorSetLayoutList const&, VulkanProgram* program)>;
VulkanDescriptorSetManager(VkDevice device, VulkanResourceAllocator* resourceAllocator);
@@ -108,3 +108,4 @@ private:
}// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
@@ -21,13 +21,34 @@
namespace filament::backend {
VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
VulkanDescriptorSetLayoutList const& descriptorSetLayouts) {
PipelineLayoutKey key = {VK_NULL_HANDLE};
VulkanDescriptorSetLayoutList const& descriptorSetLayouts, VulkanProgram* program) {
PipelineLayoutKey key = {};
uint8_t descSetLayoutCount = 0;
for (auto layoutHandle: descriptorSetLayouts) {
if (layoutHandle) {
auto layout = mAllocator->handle_cast<VulkanDescriptorSetLayout*>(layoutHandle);
key[descSetLayoutCount++] = layout->vklayout;
key.descSetLayouts[descSetLayoutCount++] = layout->vklayout;
}
}
// build the push constant layout key
uint32_t pushConstantRangeCount = program->getPushConstantRangeCount();
auto const& pushCostantRanges = program->getPushConstantRanges();
if (pushConstantRangeCount > 0) {
assert_invariant(pushConstantRangeCount <= Program::SHADER_TYPE_COUNT);
for (uint8_t i = 0; i < pushConstantRangeCount; ++i) {
auto const& range = pushCostantRanges[i];
auto& pushConstant = key.pushConstant[i];
if (range.stageFlags & VK_SHADER_STAGE_VERTEX_BIT) {
pushConstant.stage = static_cast<uint8_t>(ShaderStage::VERTEX);
}
if (range.stageFlags & VK_SHADER_STAGE_FRAGMENT_BIT) {
pushConstant.stage = static_cast<uint8_t>(ShaderStage::FRAGMENT);
}
if (range.stageFlags & VK_SHADER_STAGE_COMPUTE_BIT) {
pushConstant.stage = static_cast<uint8_t>(ShaderStage::COMPUTE);
}
pushConstant.size = range.size;
}
}
@@ -42,9 +63,11 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.setLayoutCount = (uint32_t) descSetLayoutCount,
.pSetLayouts = key.data(),
.pushConstantRangeCount = 0,
.pSetLayouts = key.descSetLayouts.data(),
.pushConstantRangeCount = pushConstantRangeCount,
.pPushConstantRanges = pushCostantRanges,
};
VkPipelineLayout layout;
vkCreatePipelineLayout(mDevice, &info, VKALLOC, &layout);
@@ -35,13 +35,29 @@ public:
void terminate() noexcept;
using PipelineLayoutKey = std::array<VkDescriptorSetLayout,
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
struct PushConstantKey {
uint8_t stage;// We have one set of push constant per shader stage (fragment, vertex, etc).
uint8_t size;
// Note that there is also an offset parameter for push constants, but
// we always assume our update range will have the offset 0.
};
struct PipelineLayoutKey {
using DescriptorSetLayoutArray = std::array<VkDescriptorSetLayout,
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
DescriptorSetLayoutArray descSetLayouts = {}; // 8 * 3
PushConstantKey pushConstant[Program::SHADER_TYPE_COUNT] = {}; // 2 * 3
uint16_t padding = 0;
};
static_assert(sizeof(PipelineLayoutKey) == 32);
VulkanPipelineLayoutCache(VulkanPipelineLayoutCache const&) = delete;
VulkanPipelineLayoutCache& operator=(VulkanPipelineLayoutCache const&) = delete;
VkPipelineLayout getLayout(VulkanDescriptorSetLayoutList const& descriptorSetLayouts);
// A pipeline layout depends on the descriptor set layout and the push constant ranges, which
// are described in the program.
VkPipelineLayout getLayout(VulkanDescriptorSetLayoutList const& descriptorSetLayouts,
VulkanProgram* program);
private:
using Timestamp = uint64_t;
+1 -1
View File
@@ -265,7 +265,7 @@ TEST_F(BackendTest, FeedbackLoops) {
for (auto rt : renderTargets) api.destroyRenderTarget(rt);
}
const uint32_t expected = 0xe93a4a07;
const uint32_t expected = 0x70695aa1;
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", sPixelHashResult, expected);
EXPECT_TRUE(sPixelHashResult == expected);
}
+51
View File
@@ -30,6 +30,7 @@
#include <private/filament/SubpassInfo.h>
#include <private/filament/Variant.h>
#include <private/filament/ConstantInfo.h>
#include <private/filament/PushConstantInfo.h>
#include <utils/CString.h>
@@ -225,6 +226,14 @@ bool MaterialParser::getConstants(utils::FixedCapacityVector<MaterialConstant>*
return ChunkMaterialConstants::unflatten(unflattener, value);
}
bool MaterialParser::getPushConstants(utils::CString* structVarName,
utils::FixedCapacityVector<MaterialPushConstant>* value) const noexcept {
auto [start, end] = mImpl.mChunkContainer.getChunkRange(filamat::MaterialPushConstants);
if (start == end) return false;
Unflattener unflattener(start, end);
return ChunkMaterialPushConstants::unflatten(unflattener, structVarName, value);
}
bool MaterialParser::getDepthWriteSet(bool* value) const noexcept {
return mImpl.getFromSimpleChunk(ChunkType::MaterialDepthWriteSet, value);
}
@@ -709,4 +718,46 @@ bool ChunkMaterialConstants::unflatten(filaflat::Unflattener& unflattener,
return true;
}
bool ChunkMaterialPushConstants::unflatten(filaflat::Unflattener& unflattener,
utils::CString* structVarName,
utils::FixedCapacityVector<MaterialPushConstant>* materialPushConstants) {
assert_invariant(materialPushConstants);
if (!unflattener.read(structVarName)) {
return false;
}
// Read number of constants.
uint64_t numConstants = 0;
if (!unflattener.read(&numConstants)) {
return false;
}
materialPushConstants->reserve(numConstants);
materialPushConstants->resize(numConstants);
for (uint64_t i = 0; i < numConstants; i++) {
CString constantName;
uint8_t constantType = 0;
uint8_t shaderStage = 0;
if (!unflattener.read(&constantName)) {
return false;
}
if (!unflattener.read(&constantType)) {
return false;
}
if (!unflattener.read(&shaderStage)) {
return false;
}
(*materialPushConstants)[i].name = constantName;
(*materialPushConstants)[i].type = static_cast<backend::ConstantType>(constantType);
(*materialPushConstants)[i].stage = static_cast<backend::ShaderStage>(shaderStage);
}
return true;
}
} // namespace filament
+8
View File
@@ -47,6 +47,7 @@ class BufferInterfaceBlock;
class SamplerInterfaceBlock;
struct SubpassInfo;
struct MaterialConstant;
struct MaterialPushConstant;
class MaterialParser {
public:
@@ -79,6 +80,8 @@ public:
bool getSamplerBlockBindings(SamplerGroupBindingInfoList* pSamplerGroupInfoList,
SamplerBindingToNameMap* pSamplerBindingToNameMap) const noexcept;
bool getConstants(utils::FixedCapacityVector<MaterialConstant>* value) const noexcept;
bool getPushConstants(utils::CString* structVarName,
utils::FixedCapacityVector<MaterialPushConstant>* value) const noexcept;
using BindingUniformInfoContainer = utils::FixedCapacityVector<
std::pair<filament::UniformBindingPoints, backend::Program::UniformInfo>>;
@@ -214,6 +217,11 @@ struct ChunkMaterialConstants {
utils::FixedCapacityVector<MaterialConstant>* materialConstants);
};
struct ChunkMaterialPushConstants {
static bool unflatten(filaflat::Unflattener& unflattener, utils::CString* structVarName,
utils::FixedCapacityVector<MaterialPushConstant>* materialPushConstants);
};
} // namespace filament
#endif // TNT_FILAMENT_MATERIALPARSER_H
+46 -6
View File
@@ -25,6 +25,7 @@
#include <private/filament/EngineEnums.h>
#include <private/filament/SamplerInterfaceBlock.h>
#include <private/filament/BufferInterfaceBlock.h>
#include <private/filament/PushConstantInfo.h>
#include <private/filament/Variant.h>
#include <filament/Material.h>
@@ -57,10 +58,9 @@
#include <array>
#include <iterator>
#include <memory>
#include <mutex>
#include <new>
#include <optional>
#include <sstream>
#include <string>
#include <string_view>
#include <unordered_map>
#include <utility>
@@ -83,18 +83,18 @@ static std::unique_ptr<MaterialParser> createParser(Backend backend,
MaterialParser::ParseResult const materialResult = materialParser->parse();
if (UTILS_UNLIKELY(materialResult == MaterialParser::ParseResult::ERROR_MISSING_BACKEND)) {
std::stringstream languageNames;
std::string languageNames;
for (auto it = languages.begin(); it != languages.end(); ++it) {
languageNames << shaderLanguageToString(*it);
languageNames.append(shaderLanguageToString(*it));
if (std::next(it) != languages.end()) {
languageNames << ", ";
languageNames.append(", ");
}
}
ASSERT_PRECONDITION(materialResult != MaterialParser::ParseResult::ERROR_MISSING_BACKEND,
"the material was not built for any of the %s backend's supported shader "
"languages (%s)\n",
backendToString(backend), languageNames.str().c_str());
backendToString(backend), languageNames.c_str());
}
if (backend == Backend::NOOP) {
@@ -298,6 +298,7 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder,
processBlendingMode(parser);
processSpecializationConstants(engine, builder, parser);
processPushConstants(engine, parser);
processDepthVariants(engine, parser);
// we can only initialize the default instance once we're initialized ourselves
@@ -576,6 +577,9 @@ Program FMaterial::getProgramWithVariants(
program.specializationConstants(mSpecializationConstants);
program.pushConstants(ShaderStage::VERTEX, mPushConstants[(uint8_t) ShaderStage::VERTEX]);
program.pushConstants(ShaderStage::FRAGMENT, mPushConstants[(uint8_t) ShaderStage::FRAGMENT]);
program.cacheId(utils::hash::combine(size_t(mCacheId), variant.key));
return program;
@@ -928,6 +932,42 @@ void FMaterial::processSpecializationConstants(FEngine& engine, Material::Builde
}
}
void FMaterial::processPushConstants(FEngine& engine, MaterialParser const* parser) {
utils::FixedCapacityVector<backend::Program::PushConstant>& vertexConstants =
mPushConstants[(uint8_t) ShaderStage::VERTEX];
utils::FixedCapacityVector<backend::Program::PushConstant>& fragmentConstants =
mPushConstants[(uint8_t) ShaderStage::FRAGMENT];
CString structVarName;
utils::FixedCapacityVector<MaterialPushConstant> pushConstants;
parser->getPushConstants(&structVarName, &pushConstants);
vertexConstants.reserve(pushConstants.size());
fragmentConstants.reserve(pushConstants.size());
constexpr size_t MAX_NAME_LEN = 60;
char buf[MAX_NAME_LEN];
uint8_t vertexCount = 0, fragmentCount = 0;
std::for_each(pushConstants.cbegin(), pushConstants.cend(),
[&](MaterialPushConstant const& constant) {
snprintf(buf, sizeof(buf), "%s.%s", structVarName.c_str(), constant.name.c_str());
switch (constant.stage) {
case ShaderStage::VERTEX:
vertexConstants.push_back({utils::CString(buf), constant.type});
vertexCount++;
break;
case ShaderStage::FRAGMENT:
fragmentConstants.push_back({utils::CString(buf), constant.type});
fragmentCount++;
break;
case ShaderStage::COMPUTE:
break;
}
});
}
void FMaterial::processDepthVariants(FEngine& engine, MaterialParser const* const parser) {
parser->hasCustomDepthShader(&mHasCustomDepthShader);
+7
View File
@@ -253,6 +253,8 @@ private:
void processSpecializationConstants(FEngine& engine, Material::Builder const& builder,
MaterialParser const* parser);
void processPushConstants(FEngine& engine, MaterialParser const* parser);
void processDepthVariants(FEngine& engine, MaterialParser const* parser);
void createAndCacheProgram(backend::Program&& p, Variant variant) const noexcept;
@@ -318,6 +320,11 @@ private:
// current specialization constants for the HwProgram
utils::FixedCapacityVector<backend::Program::SpecializationConstant> mSpecializationConstants;
// current push constants for the HwProgram
std::array<utils::FixedCapacityVector<backend::Program::PushConstant>,
backend::Program::SHADER_TYPE_COUNT>
mPushConstants;
#if FILAMENT_ENABLE_MATDBG
matdbg::MaterialKey mDebuggerId;
mutable utils::Mutex mActiveProgramsLock;
+17
View File
@@ -179,6 +179,23 @@ TEST(FilamentTest, SkinningMath) {
}
}
TEST(FilamentTest, TransformManagerSimple) {
filament::FTransformManager tcm;
EntityManager& em = EntityManager::get();
Entity root = em.create();
tcm.create(root);
auto ti = tcm.getInstance(root);
auto t = mat4f::translation(float3{ 1, 2, 3 });
auto prev = tcm.getWorldTransform(ti);
tcm.setTransform(ti, t);
auto updated = tcm.getWorldTransform(ti);
EXPECT_NE(prev, t);
EXPECT_EQ(updated, t);
}
TEST(FilamentTest, TransformManager) {
filament::FTransformManager tcm;
tcm.setAccurateTranslationsEnabled(true);
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.51.7"
spec.version = "1.51.8"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.51.7/filament-v1.51.7-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.51.8/filament-v1.51.8-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
@@ -53,6 +53,7 @@ enum UTILS_PUBLIC ChunkType : uint64_t {
MaterialAttributeInfo = charTo64bitNum("MAT_ATTR"),
MaterialProperties = charTo64bitNum("MAT_PROP"),
MaterialConstants = charTo64bitNum("MAT_CONS"),
MaterialPushConstants = charTo64bitNum("MAT_PCON"),
MaterialName = charTo64bitNum("MAT_NAME"),
MaterialVersion = charTo64bitNum("MAT_VERS"),
@@ -20,6 +20,7 @@
#include <backend/DriverEnums.h>
#include <utils/BitmaskEnum.h>
#include <utils/FixedCapacityVector.h>
#include <stddef.h>
#include <stdint.h>
@@ -71,6 +72,10 @@ enum class ReservedSpecializationConstants : uint8_t {
CONFIG_STEREO_EYE_COUNT = 8, // don't change (hardcoded in ShaderCompilerService.cpp)
};
enum class PushConstantIds {
MORPHING_BUFFER_OFFSET = 0,
};
// This value is limited by UBO size, ES3.0 only guarantees 16 KiB.
// It's also limited by the Froxelizer's record buffer data type (uint8_t).
constexpr size_t CONFIG_MAX_LIGHT_COUNT = 256;
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2023 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_PUSHCONSTANTINFO_H
#define TNT_FILAMENT_PUSHCONSTANTINFO_H
#include <backend/DriverEnums.h>
#include <utils/CString.h>
namespace filament {
struct MaterialPushConstant {
using ShaderStage = backend::ShaderStage;
using ConstantType = backend::ConstantType;
utils::CString name;
ConstantType type;
ShaderStage stage;
MaterialPushConstant() = default;
MaterialPushConstant(const char* name, ConstantType type, ShaderStage stage)
: name(name),
type(type),
stage(stage) {}
};
}
#endif // TNT_FILAMENT_PUSHCONSTANTINFO_H
+2 -1
View File
@@ -23,7 +23,8 @@ set(COMMON_PRIVATE_HDRS
src/eiff/MaterialInterfaceBlockChunk.h
src/eiff/ShaderEntry.h
src/eiff/SimpleFieldChunk.h
src/Includes.h)
src/Includes.h
src/PushConstantDefinitions.h)
set(COMMON_SRCS
src/eiff/Chunk.cpp
@@ -245,6 +245,7 @@ public:
using CullingMode = filament::backend::CullingMode;
using FeatureLevel = filament::backend::FeatureLevel;
using StereoscopicType = filament::backend::StereoscopicType;
using ShaderStage = filament::backend::ShaderStage;
enum class VariableQualifier : uint8_t {
OUT
@@ -692,6 +693,12 @@ public:
} defaultValue;
};
struct PushConstant {
utils::CString name;
ConstantType type;
ShaderStage stage;
};
static constexpr size_t MATERIAL_PROPERTIES_COUNT = filament::MATERIAL_PROPERTIES_COUNT;
using Property = filament::Property;
@@ -720,6 +727,7 @@ public:
using SubpassList = Parameter[MAX_SUBPASS_COUNT];
using BufferList = std::vector<std::unique_ptr<filament::BufferInterfaceBlock>>;
using ConstantList = std::vector<Constant>;
using PushConstantList = std::vector<PushConstant>;
// returns the number of parameters declared in this material
uint8_t getParameterCount() const noexcept { return mParameterCount; }
@@ -763,6 +771,10 @@ private:
void prepareToBuild(MaterialInfo& info) noexcept;
// Initialize internal push constants that will both be written to the shaders and material
// chunks (like user-defined spec constants).
void initPushConstants() noexcept;
// Return true if the shader is syntactically and semantically valid.
// This method finds all the properties defined in the fragment and
// vertex shaders of the material.
@@ -829,6 +841,7 @@ private:
PropertyList mProperties;
ParameterList mParameters;
ConstantList mConstants;
PushConstantList mPushConstants;
SubpassList mSubpasses;
VariableList mVariables;
OutputList mOutputs;
+26 -2
View File
@@ -20,6 +20,7 @@
#include "Includes.h"
#include "MaterialVariants.h"
#include "PushConstantDefinitions.h"
#include "shaders/SibGenerator.h"
#include "shaders/UibGenerator.h"
@@ -173,6 +174,8 @@ void MaterialBuilderBase::prepare(bool vulkanSemantics,
MaterialBuilder::MaterialBuilder() : mMaterialName("Unnamed") {
std::fill_n(mProperties, MATERIAL_PROPERTIES_COUNT, false);
mShaderModels.reset();
initPushConstants();
}
MaterialBuilder::~MaterialBuilder() = default;
@@ -660,6 +663,19 @@ void MaterialBuilder::prepareToBuild(MaterialInfo& info) noexcept {
info.userMaterialHasCustomDepth = false;
}
void MaterialBuilder::initPushConstants() noexcept {
mPushConstants.reserve(PUSH_CONSTANTS.size());
mPushConstants.resize(PUSH_CONSTANTS.size());
std::transform(PUSH_CONSTANTS.cbegin(), PUSH_CONSTANTS.cend(), mPushConstants.begin(),
[](filament::MaterialPushConstant const& inConstant) -> PushConstant {
return {
.name = inConstant.name,
.type = inConstant.type,
.stage = inConstant.stage,
};
});
}
bool MaterialBuilder::findProperties(backend::ShaderStage type,
MaterialBuilder::PropertyList& allProperties,
CodeGenParams const& semanticCodeGenParams) noexcept {
@@ -828,7 +844,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
BlobDictionary spirvDictionary;
// End: must be protected by lock
ShaderGenerator sg(mProperties, mVariables, mOutputs, mDefines, mConstants,
ShaderGenerator sg(mProperties, mVariables, mOutputs, mDefines, mConstants, mPushConstants,
mMaterialFragmentCode.getResolved(), mMaterialFragmentCode.getLineOffset(),
mMaterialVertexCode.getResolved(), mMaterialVertexCode.getLineOffset(),
mMaterialDomain);
@@ -1358,7 +1374,7 @@ bool MaterialBuilder::needsStandardDepthProgram() const noexcept {
std::string MaterialBuilder::peek(backend::ShaderStage stage,
const CodeGenParams& params, const PropertyList& properties) noexcept {
ShaderGenerator sg(properties, mVariables, mOutputs, mDefines, mConstants,
ShaderGenerator sg(properties, mVariables, mOutputs, mDefines, mConstants, mPushConstants,
mMaterialFragmentCode.getResolved(), mMaterialFragmentCode.getLineOffset(),
mMaterialVertexCode.getResolved(), mMaterialVertexCode.getLineOffset(),
mMaterialDomain);
@@ -1512,6 +1528,14 @@ void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo&
[](Constant const& c) { return MaterialConstant(c.name.c_str(), c.type); });
container.push<MaterialConstantParametersChunk>(std::move(constantsEntry));
utils::FixedCapacityVector<MaterialPushConstant> pushConstantsEntry(mPushConstants.size());
std::transform(mPushConstants.begin(), mPushConstants.end(), pushConstantsEntry.begin(),
[](PushConstant const& c) {
return MaterialPushConstant(c.name.c_str(), c.type, c.stage);
});
container.push<MaterialPushConstantParametersChunk>(
utils::CString(PUSH_CONSTANT_STRUCT_VAR_NAME), std::move(pushConstantsEntry));
// TODO: should we write the SSBO info? this would only be needed if we wanted to provide
// an interface to set [get?] values in the buffer. But we can do that easily
// with a c-struct (what about kotlin/java?). tbd.
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMAT_PUSH_CONSTANT_DEFINTITIONS_H
#define TNT_FILAMAT_PUSH_CONSTANT_DEFINTITIONS_H
#include <private/filament/EngineEnums.h>
#include <private/filament/PushConstantInfo.h>
#include <utils/FixedCapacityVector.h>
#include <tuple>
namespace filamat {
constexpr char PUSH_CONSTANT_STRUCT_VAR_NAME[] = "pushConstants";
utils::FixedCapacityVector<filament::MaterialPushConstant> const PUSH_CONSTANTS = {
{
"morphingBufferOffset",
filament::backend::ConstantType::INT,
filament::backend::ShaderStage::VERTEX,
},
};
// Make sure that the indices defined in filabridge match the actual array indices defined here.
static_assert(static_cast<uint8_t>(filament::PushConstantIds::MORPHING_BUFFER_OFFSET) == 0u);
}// namespace filamat
#endif
@@ -22,6 +22,7 @@
#include <private/filament/BufferInterfaceBlock.h>
#include <private/filament/SubpassInfo.h>
#include <private/filament/ConstantInfo.h>
#include <private/filament/PushConstantInfo.h>
#include <utility>
@@ -104,6 +105,24 @@ void MaterialConstantParametersChunk::flatten(Flattener& f) {
// ------------------------------------------------------------------------------------------------
MaterialPushConstantParametersChunk::MaterialPushConstantParametersChunk(
CString const& structVarName, utils::FixedCapacityVector<MaterialPushConstant> constants)
: Chunk(ChunkType::MaterialPushConstants),
mStructVarName(structVarName),
mConstants(std::move(constants)) {}
void MaterialPushConstantParametersChunk::flatten(Flattener& f) {
f.writeString(mStructVarName.c_str());
f.writeUint64(mConstants.size());
for (const auto& constant: mConstants) {
f.writeString(constant.name.c_str());
f.writeUint8(static_cast<uint8_t>(constant.type));
f.writeUint8(static_cast<uint8_t>(constant.stage));
}
}
// ------------------------------------------------------------------------------------------------
MaterialUniformBlockBindingsChunk::MaterialUniformBlockBindingsChunk(
utils::FixedCapacityVector<std::pair<std::string_view, filament::UniformBindingPoints>> list)
: Chunk(ChunkType::MaterialUniformBindings),
@@ -31,6 +31,7 @@ class SamplerInterfaceBlock;
class BufferInterfaceBlock;
struct SubpassInfo;
struct MaterialConstant;
struct MaterialPushConstant;
} // namespace filament
namespace filamat {
@@ -88,6 +89,21 @@ private:
// ------------------------------------------------------------------------------------------------
class MaterialPushConstantParametersChunk final : public Chunk {
public:
explicit MaterialPushConstantParametersChunk(utils::CString const& structVarName,
utils::FixedCapacityVector<filament::MaterialPushConstant> constants);
~MaterialPushConstantParametersChunk() final = default;
private:
void flatten(Flattener&) final;
utils::CString mStructVarName;
utils::FixedCapacityVector<filament::MaterialPushConstant> mConstants;
};
// ------------------------------------------------------------------------------------------------
class MaterialUniformBlockBindingsChunk final : public Chunk {
using Container = utils::FixedCapacityVector<
std::pair<std::string_view, filament::UniformBindingPoints>>;
+41 -2
View File
@@ -17,6 +17,7 @@
#include "CodeGenerator.h"
#include "MaterialInfo.h"
#include "../PushConstantDefinitions.h"
#include "generated/shaders.h"
@@ -454,8 +455,8 @@ io::sstream& CodeGenerator::generateVariable(io::sstream& out, ShaderStage stage
}
io::sstream& CodeGenerator::generateShaderInputs(io::sstream& out, ShaderStage type,
const AttributeBitset& attributes, Interpolation interpolation) const {
const AttributeBitset& attributes, Interpolation interpolation,
MaterialBuilder::PushConstantList const& pushConstants) const {
auto const& attributeDatabase = MaterialBuilder::getAttributeDatabase();
const char* shading = getInterpolationQualifier(interpolation);
@@ -479,6 +480,9 @@ io::sstream& CodeGenerator::generateShaderInputs(io::sstream& out, ShaderStage t
}
out << getTypeName(attribute.type) << " " << attribute.getAttributeName() << ";\n";
});
out << "\n";
generatePushConstants(out, pushConstants, attributes.size());
}
out << "\n";
@@ -906,6 +910,41 @@ utils::io::sstream& CodeGenerator::generateSpecializationConstant(utils::io::sst
return out;
}
utils::io::sstream& CodeGenerator::generatePushConstants(utils::io::sstream& out,
MaterialBuilder::PushConstantList const& pushConstants, size_t const layoutLocation) const {
static constexpr char const* STRUCT_NAME = "Constants";
bool const outputSpirv =
mTargetLanguage == TargetLanguage::SPIRV && mTargetApi != TargetApi::OPENGL;
auto const getType = [](ConstantType const& type) {
switch (type) {
case ConstantType::BOOL:
return "bool";
case ConstantType::INT:
return "int";
case ConstantType::FLOAT:
return "float";
}
};
if (outputSpirv) {
out << "layout(push_constant) uniform " << STRUCT_NAME << " {\n ";
} else {
out << "struct " << STRUCT_NAME << " {\n";
}
for (auto const& constant: pushConstants) {
out << getType(constant.type) << " " << constant.name.c_str() << ";\n";
}
if (outputSpirv) {
out << "} " << PUSH_CONSTANT_STRUCT_VAR_NAME << ";\n";
} else {
out << "};\n";
out << "LAYOUT_LOCATION(" << static_cast<int>(layoutLocation) << ") uniform " << STRUCT_NAME
<< " " << PUSH_CONSTANT_STRUCT_VAR_NAME << ";\n";
}
return out;
}
io::sstream& CodeGenerator::generateMaterialProperty(io::sstream& out,
MaterialBuilder::Property property, bool isSet) {
+6 -1
View File
@@ -107,7 +107,8 @@ public:
// generate declarations for non-custom "in" variables
utils::io::sstream& generateShaderInputs(utils::io::sstream& out, ShaderStage type,
const filament::AttributeBitset& attributes, filament::Interpolation interpolation) const;
const filament::AttributeBitset& attributes, filament::Interpolation interpolation,
MaterialBuilder::PushConstantList const& pushConstants) const;
static utils::io::sstream& generatePostProcessInputs(utils::io::sstream& out, ShaderStage type);
// generate declarations for custom output variables
@@ -156,6 +157,10 @@ public:
utils::io::sstream& generateSpecializationConstant(utils::io::sstream& out,
const char* name, uint32_t id, std::variant<int, float, bool> value) const;
utils::io::sstream& generatePushConstants(utils::io::sstream& out,
MaterialBuilder::PushConstantList const& pushConstants,
size_t const layoutLocation) const;
static utils::io::sstream& generatePostProcessGetters(utils::io::sstream& out, ShaderStage type);
static utils::io::sstream& generateGetters(utils::io::sstream& out, ShaderStage stage);
static utils::io::sstream& generateParameters(utils::io::sstream& out, ShaderStage type);
+19 -4
View File
@@ -304,6 +304,7 @@ ShaderGenerator::ShaderGenerator(
MaterialBuilder::OutputList const& outputs,
MaterialBuilder::PreprocessorDefineList const& defines,
MaterialBuilder::ConstantList const& constants,
MaterialBuilder::PushConstantList const& pushConstants,
CString const& materialCode, size_t lineOffset,
CString const& materialVertexCode, size_t vertexLineOffset,
MaterialBuilder::MaterialDomain materialDomain) noexcept {
@@ -325,6 +326,7 @@ ShaderGenerator::ShaderGenerator(
mMaterialDomain = materialDomain;
mDefines = defines;
mConstants = constants;
mPushConstants = pushConstants;
if (mMaterialFragmentCode.empty()) {
if (mMaterialDomain == MaterialBuilder::MaterialDomain::SURFACE) {
@@ -418,7 +420,15 @@ std::string ShaderGenerator::createVertexProgram(ShaderModel shaderModel,
attributes.set(VertexAttribute::MORPH_TANGENTS_3);
}
}
cg.generateShaderInputs(vs, ShaderStage::VERTEX, attributes, interpolation);
MaterialBuilder::PushConstantList vertexPushConstants;
std::copy_if(mPushConstants.begin(), mPushConstants.end(),
std::back_insert_iterator<MaterialBuilder::PushConstantList>(vertexPushConstants),
[](MaterialBuilder::PushConstant const& constant) {
return constant.stage == ShaderStage::VERTEX;
});
cg.generateShaderInputs(vs, ShaderStage::VERTEX, attributes, interpolation,
vertexPushConstants);
CodeGenerator::generateCommonTypes(vs, ShaderStage::VERTEX);
@@ -519,9 +529,14 @@ std::string ShaderGenerator::createFragmentProgram(ShaderModel shaderModel,
generateSurfaceMaterialVariantProperties(fs, mProperties, mDefines);
cg.generateShaderInputs(fs, ShaderStage::FRAGMENT,
material.requiredAttributes, interpolation);
MaterialBuilder::PushConstantList fragmentPushConstants;
std::copy_if(mPushConstants.begin(), mPushConstants.end(),
std::back_insert_iterator<MaterialBuilder::PushConstantList>(fragmentPushConstants),
[](MaterialBuilder::PushConstant const& constant) {
return constant.stage == ShaderStage::FRAGMENT;
});
cg.generateShaderInputs(fs, ShaderStage::FRAGMENT, material.requiredAttributes, interpolation,
fragmentPushConstants);
CodeGenerator::generateCommonTypes(fs, ShaderStage::FRAGMENT);
@@ -43,6 +43,7 @@ public:
MaterialBuilder::OutputList const& outputs,
MaterialBuilder::PreprocessorDefineList const& defines,
MaterialBuilder::ConstantList const& constants,
MaterialBuilder::PushConstantList const& pushConstants,
utils::CString const& materialCode,
size_t lineOffset,
utils::CString const& materialVertexCode,
@@ -125,6 +126,7 @@ private:
MaterialBuilder::MaterialDomain mMaterialDomain;
MaterialBuilder::PreprocessorDefineList mDefines;
MaterialBuilder::ConstantList mConstants;
MaterialBuilder::PushConstantList mPushConstants;
utils::CString mMaterialFragmentCode; // fragment or compute code
utils::CString mMaterialVertexCode;
size_t mMaterialLineOffset;
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.51.7",
"version": "1.51.8",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",