// Copyright Epic Games, Inc. All Rights Reserved. #include "MosisPhoneComponent.h" #include "MosisSDK.h" #include "MosisPhoneTexture.h" #include "Materials/MaterialInstanceDynamic.h" #include "RenderingThread.h" #if PLATFORM_ANDROID #include "MosisClient.h" #endif DEFINE_LOG_CATEGORY_STATIC(LogMosisPhone, Log, All); UMosisPhoneComponent::UMosisPhoneComponent() { PrimaryComponentTick.bCanEverTick = true; PrimaryComponentTick.bStartWithTickEnabled = true; } void UMosisPhoneComponent::BeginPlay() { Super::BeginPlay(); UE_LOG(LogMosisPhone, Log, TEXT("BeginPlay")); #if PLATFORM_ANDROID // Get the client and set up callbacks auto Client = FMosisSDKModule::GetClient(); if (Client) { // Use weak pointer to prevent dangling references in callbacks TWeakObjectPtr WeakThis(this); // Set up callbacks (these run on binder thread) Client->SetBufferCallback([WeakThis](AHardwareBuffer* buffer) { if (WeakThis.IsValid()) { WeakThis->bNeedsTextureCreate.Store(true); } }); Client->SetFrameCallback([WeakThis]() { if (WeakThis.IsValid()) { WeakThis->bPendingTextureUpdate.Store(true); } }); UE_LOG(LogMosisPhone, Log, TEXT("BeginPlay: Callbacks registered")); // Check if buffer is already available (we may have missed the callback) if (Client->GetHardwareBuffer() != nullptr) { UE_LOG(LogMosisPhone, Log, TEXT("BeginPlay: Buffer already available, triggering update")); bNeedsTextureCreate.Store(true); } } else { UE_LOG(LogMosisPhone, Warning, TEXT("BeginPlay: MosisClient not available yet")); } #endif } void UMosisPhoneComponent::EndPlay(const EEndPlayReason::Type EndPlayReason) { UE_LOG(LogMosisPhone, Log, TEXT("EndPlay")); #if PLATFORM_ANDROID // Clear callbacks auto Client = FMosisSDKModule::GetClient(); if (Client) { Client->SetBufferCallback(nullptr); Client->SetFrameCallback(nullptr); } #endif // Release texture PhoneTexture = nullptr; Super::EndPlay(EndPlayReason); } void UMosisPhoneComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); #if PLATFORM_ANDROID // Check if we need to create texture from new buffer if (bNeedsTextureCreate.Exchange(false)) { OnBufferAvailable(); } // Check if we need to update texture for new frame if (bPendingTextureUpdate.Exchange(false) && PhoneTexture) { OnFrameAvailable(); } #endif } bool UMosisPhoneComponent::IsConnected() const { #if PLATFORM_ANDROID auto Client = FMosisSDKModule::GetClient(); return Client && Client->IsInitialized() && Client->GetHardwareBuffer() != nullptr; #else return false; #endif } void UMosisPhoneComponent::SendTouch(FVector2D NormalizedUV, EMosisTouchType TouchType) { #if PLATFORM_ANDROID auto Client = FMosisSDKModule::GetClient(); if (!Client) { return; } float X = FMath::Clamp(NormalizedUV.X, 0.0f, 1.0f); float Y = FMath::Clamp(NormalizedUV.Y, 0.0f, 1.0f); switch (TouchType) { case EMosisTouchType::Down: Client->SendTouchDown(X, Y); break; case EMosisTouchType::Move: Client->SendTouchMove(X, Y); break; case EMosisTouchType::Up: Client->SendTouchUp(X, Y); break; } #endif } void UMosisPhoneComponent::OnBufferAvailable() { #if PLATFORM_ANDROID auto Client = FMosisSDKModule::GetClient(); if (!Client) { return; } AHardwareBuffer* Buffer = Client->GetHardwareBuffer(); if (!Buffer) { UE_LOG(LogMosisPhone, Warning, TEXT("OnBufferAvailable: No buffer")); return; } // Get buffer dimensions AHardwareBuffer_Desc Desc{}; AHardwareBuffer_describe(Buffer, &Desc); ScreenWidth = Desc.width; ScreenHeight = Desc.height; UE_LOG(LogMosisPhone, Log, TEXT("OnBufferAvailable: %dx%d"), ScreenWidth, ScreenHeight); // Create phone texture if needed if (!PhoneTexture) { PhoneTexture = NewObject(this); PhoneTexture->Initialize(ScreenWidth, ScreenHeight); UE_LOG(LogMosisPhone, Log, TEXT("OnBufferAvailable: Created phone texture")); } // Import the hardware buffer for GPU-accelerated updates PhoneTexture->ImportHardwareBuffer(Buffer); // Update material if set if (PhoneMaterial && PhoneTexture) { PhoneMaterial->SetTextureParameterValue(TextureParameterName, PhoneTexture); } #endif } void UMosisPhoneComponent::OnFrameAvailable() { #if PLATFORM_ANDROID if (!PhoneTexture || !PhoneTexture->HasImportedBuffer()) { return; } // Perform GPU-to-GPU copy from imported hardware buffer PhoneTexture->CopyFromImportedBuffer(); // Log occasionally to avoid spam static int32 FrameCount = 0; if (++FrameCount % 60 == 0) { UE_LOG(LogMosisPhone, Log, TEXT("OnFrameAvailable: Frame %d (GPU copy)"), FrameCount); } #endif } void UMosisPhoneComponent::UpdateTextureOnRenderThread() { // No longer needed - handled by UMosisPhoneTexture } UTexture* UMosisPhoneComponent::GetPhoneTexture() const { return PhoneTexture; }