Skip to content

Commit

Permalink
Merge branch 'main' into ma/platform-external-image
Browse files Browse the repository at this point in the history
  • Loading branch information
pixelflinger authored Feb 3, 2025
2 parents 5be382a + b780cb3 commit efa48b4
Show file tree
Hide file tree
Showing 45 changed files with 619 additions and 236 deletions.
1 change: 1 addition & 0 deletions NEW_RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut

- backend: New platform API to better handle external textures [⚠️ **New Material Version**]
- matdbg: Add support for debugging ESSL 1.0 shaders
2 changes: 1 addition & 1 deletion filament/backend/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,11 @@ if (FILAMENT_SUPPORTS_METAL)
src/metal/MetalEnums.mm
src/metal/MetalExternalImage.mm
src/metal/MetalHandles.mm
src/metal/MetalPlatform.mm
src/metal/MetalShaderCompiler.mm
src/metal/MetalState.mm
src/metal/MetalTimerQuery.mm
src/metal/MetalUtils.mm
src/metal/PlatformMetal.mm
)

set(METAL_CPP_SRCS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
* limitations under the License.
*/

#ifndef TNT_FILAMENT_BACKEND_PRIVATE_METALPLATFORM_H
#define TNT_FILAMENT_BACKEND_PRIVATE_METALPLATFORM_H
#ifndef TNT_FILAMENT_BACKEND_PRIVATE_PLATFORMMETAL_H
#define TNT_FILAMENT_BACKEND_PRIVATE_PLATFORMMETAL_H

#include <backend/DriverEnums.h>
#include <backend/Platform.h>
Expand All @@ -24,9 +24,12 @@

namespace filament::backend {

class MetalPlatform final : public Platform {
struct PlatformMetalImpl;

class PlatformMetal final : public Platform {
public:
~MetalPlatform() override;
PlatformMetal();
~PlatformMetal() noexcept override;

Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) noexcept override;
int getOSVersion() const noexcept override { return 0; }
Expand Down Expand Up @@ -54,11 +57,30 @@ class MetalPlatform final : public Platform {
*/
id<MTLCommandBuffer> createAndEnqueueCommandBuffer() noexcept;

private:
id<MTLCommandQueue> mCommandQueue = nil;
/**
* The action to take if a Drawable cannot be acquired.
*
* Each frame rendered requires a CAMetalDrawable texture, which is presented on-screen at the
* completion of each frame. These are limited and provided round-robin style by the system.
*/
enum class DrawableFailureBehavior : uint8_t {
/**
* Terminates the application and reports an error message (default).
*/
PANIC,
/*
* Aborts execution of the current frame. The Metal backend will attempt to acquire a new
* drawable at the next frame.
*/
ABORT_FRAME
};
void setDrawableFailureBehavior(DrawableFailureBehavior behavior) noexcept;
DrawableFailureBehavior getDrawableFailureBehavior() const noexcept;

private:
PlatformMetalImpl* pImpl = nullptr;
};

} // namespace filament::backend

#endif // TNT_FILAMENT_BACKEND_PRIVATE_METALPLATFORM_H
#endif // TNT_FILAMENT_BACKEND_PRIVATE_PLATFORMMETAL_H
10 changes: 10 additions & 0 deletions filament/backend/include/private/backend/Driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ class Driver {

virtual ShaderModel getShaderModel() const noexcept = 0;

// The shader language used for shaders for this driver, used to inform matdbg.
//
// For OpenGL, this distinguishes whether the driver's shaders are powered by ESSL1 or ESSL3.
// This information is used by matdbg to display the correct shader code to the web UI and patch
// the correct chunk when rebuilding shaders live.
//
// Metal shaders can either be MSL or Metal libraries, but at time of writing, matdbg can only
// interface with MSL.
virtual ShaderLanguage getShaderLanguage() const noexcept = 0;

// Returns the dispatcher. This is only called once during initialization of the CommandStream,
// so it doesn't matter that it's virtual.
virtual Dispatcher getDispatcher() const noexcept = 0;
Expand Down
42 changes: 14 additions & 28 deletions filament/backend/include/private/backend/VirtualMachineEnv.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,25 @@
#define TNT_FILAMENT_DRIVER_ANDROID_VIRTUAL_MACHINE_ENV_H

#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Mutex.h>

#include <jni.h>

namespace filament {

class VirtualMachineEnv {
public:
// must be called before VirtualMachineEnv::get() from a thread that is attached to the JavaVM
static jint JNI_OnLoad(JavaVM* vm) noexcept;

static VirtualMachineEnv& get() noexcept {
// declaring this thread local, will ensure it's destroyed with the calling thread
static thread_local VirtualMachineEnv instance;
return instance;
}
// must be called on backend thread
static VirtualMachineEnv& get() noexcept;

static JNIEnv* getThreadEnvironment() noexcept {
JNIEnv* env;
assert_invariant(sVirtualMachine);
if (sVirtualMachine->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return nullptr; // this should not happen
}
return env;
}
// can be called from any thread that already has a JniEnv
static JNIEnv* getThreadEnvironment() noexcept;

inline JNIEnv* getEnvironment() noexcept {
assert_invariant(mVirtualMachine);
// must be called from the backend thread
JNIEnv* getEnvironment() noexcept {
JNIEnv* env = mJniEnv;
if (UTILS_UNLIKELY(!env)) {
return getEnvironmentSlow();
Expand All @@ -55,20 +47,14 @@ class VirtualMachineEnv {
static void handleException(JNIEnv* env) noexcept;

private:
VirtualMachineEnv() noexcept : mVirtualMachine(sVirtualMachine) {
// We're not initializing the JVM here -- but we could -- because most of the time
// we don't need the jvm. Instead we do the initialization on first use. This means we could get
// a nasty slow down the very first time, but we'll live with it for now.
}

~VirtualMachineEnv() {
if (mVirtualMachine) {
mVirtualMachine->DetachCurrentThread();
}
}

explicit VirtualMachineEnv(JavaVM* vm) noexcept;
~VirtualMachineEnv() noexcept;
JNIEnv* getEnvironmentSlow() noexcept;

static utils::Mutex sLock;
static JavaVM* sVirtualMachine;
static JavaVM* getVirtualMachine();

JNIEnv* mJniEnv = nullptr;
JavaVM* mVirtualMachine = nullptr;
};
Expand Down
89 changes: 77 additions & 12 deletions filament/backend/src/VirtualMachineEnv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,28 @@

#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Mutex.h>
#include <utils/Panic.h>

#include <jni.h>

#include <mutex>

namespace filament {

JavaVM* VirtualMachineEnv::sVirtualMachine = nullptr;
using namespace utils;

// This Mutex shouldn't be subject to the Static Initialization Order Fiasco because its initial state is
// a single int initialized to 0.
/* static*/ Mutex VirtualMachineEnv::sLock;
/* static*/ JavaVM* VirtualMachineEnv::sVirtualMachine = nullptr;

UTILS_NOINLINE
JavaVM* VirtualMachineEnv::getVirtualMachine() {
std::lock_guard const lock(sLock);
assert_invariant(sVirtualMachine);
return sVirtualMachine;
}

/*
* This is typically called by filament_jni.so when it is loaded. If filament_jni.so is not used,
Expand All @@ -35,33 +51,82 @@ JavaVM* VirtualMachineEnv::sVirtualMachine = nullptr;
UTILS_PUBLIC
UTILS_NOINLINE
jint VirtualMachineEnv::JNI_OnLoad(JavaVM* vm) noexcept {
JNIEnv* env = nullptr;
if (UTILS_UNLIKELY(vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK)) {
// this should not happen
return -1;
std::lock_guard const lock(sLock);
if (sVirtualMachine) {
// It doesn't make sense for JNI_OnLoad() to be called more than once
return JNI_VERSION_1_6;
}

// Here we check this VM at least has JNI_VERSION_1_6
JNIEnv* env = nullptr;
jint const result = vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);

FILAMENT_CHECK_POSTCONDITION(result == JNI_OK)
<< "Couldn't get JniEnv* from the VM, error = " << result;

sVirtualMachine = vm;
return JNI_VERSION_1_6;
}

UTILS_NOINLINE
void VirtualMachineEnv::handleException(JNIEnv* const env) noexcept {
if (UTILS_UNLIKELY(env->ExceptionCheck())) {
env->ExceptionDescribe();
env->ExceptionClear();
VirtualMachineEnv& VirtualMachineEnv::get() noexcept {
JavaVM* const vm = getVirtualMachine();
// declaring this thread local, will ensure it's destroyed with the calling thread
thread_local VirtualMachineEnv instance{ vm };
return instance;
}

UTILS_NOINLINE
JNIEnv* VirtualMachineEnv::getThreadEnvironment() noexcept {
JavaVM* const vm = getVirtualMachine();
JNIEnv* env = nullptr;
jint const result = vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);

FILAMENT_CHECK_POSTCONDITION(result == JNI_OK)
<< "Couldn't get JniEnv* from the VM, error = " << result;

return env;
}

VirtualMachineEnv::VirtualMachineEnv(JavaVM* vm) noexcept : mVirtualMachine(vm) {
// We're not initializing the JVM here -- but we could -- because most of the time
// we don't need the jvm. Instead, we do the initialization on first use. This means we could get
// a nasty slow down the very first time, but we'll live with it for now.
}

VirtualMachineEnv::~VirtualMachineEnv() noexcept {
if (mVirtualMachine) {
mVirtualMachine->DetachCurrentThread();
}
}

UTILS_NOINLINE
JNIEnv* VirtualMachineEnv::getEnvironmentSlow() noexcept {
FILAMENT_CHECK_PRECONDITION(mVirtualMachine)
<< "JNI_OnLoad() has not been called";

#if defined(__ANDROID__)
mVirtualMachine->AttachCurrentThread(&mJniEnv, nullptr);
jint const result = mVirtualMachine->AttachCurrentThread(&mJniEnv, nullptr);
#else
mVirtualMachine->AttachCurrentThread(reinterpret_cast<void**>(&mJniEnv), nullptr);
jint const result = mVirtualMachine->AttachCurrentThread(reinterpret_cast<void**>(&mJniEnv), nullptr);
#endif
assert_invariant(mJniEnv);

FILAMENT_CHECK_POSTCONDITION(result == JNI_OK)
<< "JavaVM::AttachCurrentThread failed with error " << result;

FILAMENT_CHECK_POSTCONDITION(mJniEnv)
<< "JavaVM::AttachCurrentThread returned a null mJniEnv";

return mJniEnv;
}

UTILS_NOINLINE
void VirtualMachineEnv::handleException(JNIEnv* const env) noexcept {
if (UTILS_UNLIKELY(env->ExceptionCheck())) {
env->ExceptionDescribe();
env->ExceptionClear();
}
}

} // namespace filament

10 changes: 5 additions & 5 deletions filament/backend/src/metal/MetalBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
#define TNT_FILAMENT_DRIVER_METALBUFFER_H

#include "MetalContext.h"
#include "MetalPlatform.h"

#include <backend/DriverEnums.h>
#include <backend/platforms/PlatformMetal.h>

#include <Metal/Metal.h>

Expand Down Expand Up @@ -54,12 +54,12 @@ class ScopedAllocationTimer {
}
}

static void setPlatform(MetalPlatform* p) { platform = p; }
static void setPlatform(PlatformMetal* p) { platform = p; }

private:
typedef std::chrono::steady_clock clock_t;

static MetalPlatform* platform;
static PlatformMetal* platform;

std::chrono::time_point<clock_t> mBeginning;
const char* mName;
Expand Down Expand Up @@ -141,7 +141,7 @@ class TrackedMetalBuffer {
assert_invariant(type != Type::NONE);
return aliveBuffers[toIndex(type)];
}
static void setPlatform(MetalPlatform* p) { platform = p; }
static void setPlatform(PlatformMetal* p) { platform = p; }

private:
void swap(TrackedMetalBuffer& other) noexcept {
Expand All @@ -152,7 +152,7 @@ class TrackedMetalBuffer {
id<MTLBuffer> mBuffer;
Type mType = Type::NONE;

static MetalPlatform* platform;
static PlatformMetal* platform;
static std::array<uint64_t, TypeCount> aliveBuffers;
};

Expand Down
4 changes: 2 additions & 2 deletions filament/backend/src/metal/MetalBuffer.mm
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
namespace backend {

std::array<uint64_t, TrackedMetalBuffer::TypeCount> TrackedMetalBuffer::aliveBuffers = { 0 };
MetalPlatform* TrackedMetalBuffer::platform = nullptr;
MetalPlatform* ScopedAllocationTimer::platform = nullptr;
PlatformMetal* TrackedMetalBuffer::platform = nullptr;
PlatformMetal* ScopedAllocationTimer::platform = nullptr;

MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType, BufferUsage usage,
size_t size, bool forceGpuBuffer)
Expand Down
2 changes: 2 additions & 0 deletions filament/backend/src/metal/MetalContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ struct MetalContext {
std::atomic<uint64_t> latestCompletedCommandBufferId = 0;
id<MTLCommandBuffer> pendingCommandBuffer = nil;
id<MTLRenderCommandEncoder> currentRenderPassEncoder = nil;
uint32_t currentFrame = 0;

std::atomic<bool> memorylessLimitsReached = false;

Expand Down Expand Up @@ -156,6 +157,7 @@ struct MetalContext {
RenderPassFlags currentRenderPassFlags;
MetalRenderTarget* currentRenderTarget = nullptr;
bool validPipelineBound = false;
bool currentRenderPassAbandoned = false;

// State trackers.
PipelineStateTracker pipelineState;
Expand Down
9 changes: 5 additions & 4 deletions filament/backend/src/metal/MetalDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
namespace filament {
namespace backend {

class MetalPlatform;
class PlatformMetal;

class MetalBuffer;
class MetalProgram;
Expand All @@ -51,22 +51,23 @@ struct BufferState;
#endif

class MetalDriver final : public DriverBase {
explicit MetalDriver(MetalPlatform* platform, const Platform::DriverConfig& driverConfig) noexcept;
explicit MetalDriver(PlatformMetal* platform, const Platform::DriverConfig& driverConfig) noexcept;
~MetalDriver() noexcept override;
Dispatcher getDispatcher() const noexcept final;

public:
static Driver* create(MetalPlatform* platform, const Platform::DriverConfig& driverConfig);
static Driver* create(PlatformMetal* platform, const Platform::DriverConfig& driverConfig);

private:

friend class MetalSwapChain;
friend struct MetalDescriptorSet;

MetalPlatform& mPlatform;
PlatformMetal& mPlatform;
MetalContext* mContext;

ShaderModel getShaderModel() const noexcept final;
ShaderLanguage getShaderLanguage() const noexcept final;

// Overrides the default implementation by wrapping the call to fn in an @autoreleasepool block.
void execute(std::function<void(void)> const& fn) noexcept final;
Expand Down
Loading

0 comments on commit efa48b4

Please sign in to comment.