implement GPU texture copy and add build script with launch command
This commit is contained in:
@@ -56,6 +56,11 @@ void AMosisPhoneActor::BeginPlay()
|
|||||||
{
|
{
|
||||||
// Default plane is 100x100 units, so bounds are -50 to 50 in X and Y
|
// Default plane is 100x100 units, so bounds are -50 to 50 in X and Y
|
||||||
ScreenBoundsLocal = FVector4(-50.0f, -50.0f, 50.0f, 50.0f);
|
ScreenBoundsLocal = FVector4(-50.0f, -50.0f, 50.0f, 50.0f);
|
||||||
|
|
||||||
|
// Flip the mesh on Y axis to correct texture orientation
|
||||||
|
// (OpenGL renders with origin at bottom-left, Vulkan/UE5 expects top-left)
|
||||||
|
FVector CurrentScale = PhoneMesh->GetRelativeScale3D();
|
||||||
|
PhoneMesh->SetRelativeScale3D(FVector(CurrentScale.X, -CurrentScale.Y, CurrentScale.Z));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create dynamic material for the screen
|
// Create dynamic material for the screen
|
||||||
@@ -143,5 +148,8 @@ bool AMosisPhoneActor::WorldToPhoneUV(FVector WorldLocation, FVector2D& OutUV) c
|
|||||||
OutUV.X = (LocalLocation.X - MinX) / (MaxX - MinX);
|
OutUV.X = (LocalLocation.X - MinX) / (MaxX - MinX);
|
||||||
OutUV.Y = (LocalLocation.Y - MinY) / (MaxY - MinY);
|
OutUV.Y = (LocalLocation.Y - MinY) / (MaxY - MinY);
|
||||||
|
|
||||||
|
// Flip Y to match the flipped mesh scale (corrects for OpenGL vs Vulkan coordinate systems)
|
||||||
|
OutUV.Y = 1.0f - OutUV.Y;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,8 +171,8 @@ void UMosisPhoneComponent::OnBufferAvailable()
|
|||||||
UE_LOG(LogMosisPhone, Log, TEXT("OnBufferAvailable: Created phone texture"));
|
UE_LOG(LogMosisPhone, Log, TEXT("OnBufferAvailable: Created phone texture"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import the hardware buffer
|
// Import the hardware buffer for GPU-accelerated updates
|
||||||
PhoneTexture->UpdateFromHardwareBuffer(Buffer);
|
PhoneTexture->ImportHardwareBuffer(Buffer);
|
||||||
|
|
||||||
// Update material if set
|
// Update material if set
|
||||||
if (PhoneMaterial && PhoneTexture)
|
if (PhoneMaterial && PhoneTexture)
|
||||||
@@ -185,20 +185,19 @@ void UMosisPhoneComponent::OnBufferAvailable()
|
|||||||
void UMosisPhoneComponent::OnFrameAvailable()
|
void UMosisPhoneComponent::OnFrameAvailable()
|
||||||
{
|
{
|
||||||
#if PLATFORM_ANDROID
|
#if PLATFORM_ANDROID
|
||||||
if (!PhoneTexture)
|
if (!PhoneTexture || !PhoneTexture->HasImportedBuffer())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify texture that a new frame is available
|
// Perform GPU-to-GPU copy from imported hardware buffer
|
||||||
// This schedules the render thread copy
|
PhoneTexture->CopyFromImportedBuffer();
|
||||||
PhoneTexture->NotifyFrameAvailable();
|
|
||||||
|
|
||||||
// Log occasionally to avoid spam
|
// Log occasionally to avoid spam
|
||||||
static int32 FrameCount = 0;
|
static int32 FrameCount = 0;
|
||||||
if (++FrameCount % 60 == 0)
|
if (++FrameCount % 60 == 0)
|
||||||
{
|
{
|
||||||
UE_LOG(LogMosisPhone, Log, TEXT("OnFrameAvailable: Frame %d"), FrameCount);
|
UE_LOG(LogMosisPhone, Log, TEXT("OnFrameAvailable: Frame %d (GPU copy)"), FrameCount);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,21 +2,40 @@
|
|||||||
|
|
||||||
#include "MosisPhoneTexture.h"
|
#include "MosisPhoneTexture.h"
|
||||||
#include "RenderingThread.h"
|
#include "RenderingThread.h"
|
||||||
|
#include "RHICommandList.h"
|
||||||
|
|
||||||
|
#if PLATFORM_ANDROID
|
||||||
|
#include "IVulkanDynamicRHI.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
DEFINE_LOG_CATEGORY_STATIC(LogMosisPhoneTexture, Log, All);
|
DEFINE_LOG_CATEGORY_STATIC(LogMosisPhoneTexture, Log, All);
|
||||||
|
|
||||||
UMosisPhoneTexture::UMosisPhoneTexture()
|
UMosisPhoneTexture::UMosisPhoneTexture()
|
||||||
{
|
{
|
||||||
// UTexture2DDynamic defaults are fine
|
|
||||||
SRGB = true;
|
SRGB = true;
|
||||||
Filter = TF_Bilinear;
|
Filter = TF_Bilinear;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UMosisPhoneTexture::~UMosisPhoneTexture()
|
||||||
|
{
|
||||||
|
#if PLATFORM_ANDROID
|
||||||
|
// Release imported texture RHI resource
|
||||||
|
if (ImportedTextureRHI.IsValid())
|
||||||
|
{
|
||||||
|
ImportedTextureRHI.SafeRelease();
|
||||||
|
}
|
||||||
|
CurrentBuffer = nullptr;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void UMosisPhoneTexture::Initialize(uint32 InWidth, uint32 InHeight)
|
void UMosisPhoneTexture::Initialize(uint32 InWidth, uint32 InHeight)
|
||||||
{
|
{
|
||||||
UE_LOG(LogMosisPhoneTexture, Log, TEXT("Initialize: %dx%d"), InWidth, InHeight);
|
UE_LOG(LogMosisPhoneTexture, Log, TEXT("Initialize: %dx%d"), InWidth, InHeight);
|
||||||
|
|
||||||
// Use UTexture2DDynamic's Init method
|
TextureWidth = InWidth;
|
||||||
|
TextureHeight = InHeight;
|
||||||
|
|
||||||
|
// Initialize the UTexture2DDynamic base - this creates our destination texture
|
||||||
Init(InWidth, InHeight, PF_R8G8B8A8, false);
|
Init(InWidth, InHeight, PF_R8G8B8A8, false);
|
||||||
|
|
||||||
bIsReady = true;
|
bIsReady = true;
|
||||||
@@ -24,131 +43,118 @@ void UMosisPhoneTexture::Initialize(uint32 InWidth, uint32 InHeight)
|
|||||||
|
|
||||||
#if PLATFORM_ANDROID
|
#if PLATFORM_ANDROID
|
||||||
|
|
||||||
void UMosisPhoneTexture::UpdateFromHardwareBuffer(AHardwareBuffer* Buffer)
|
void UMosisPhoneTexture::ImportHardwareBuffer(AHardwareBuffer* Buffer)
|
||||||
{
|
{
|
||||||
if (!Buffer)
|
if (!Buffer)
|
||||||
{
|
{
|
||||||
UE_LOG(LogMosisPhoneTexture, Warning, TEXT("UpdateFromHardwareBuffer: null buffer"));
|
UE_LOG(LogMosisPhoneTexture, Warning, TEXT("ImportHardwareBuffer: null buffer"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CurrentBuffer = Buffer;
|
|
||||||
|
|
||||||
// Get buffer dimensions
|
// Get buffer dimensions
|
||||||
AHardwareBuffer_Desc Desc{};
|
AHardwareBuffer_Desc Desc{};
|
||||||
AHardwareBuffer_describe(Buffer, &Desc);
|
AHardwareBuffer_describe(Buffer, &Desc);
|
||||||
|
|
||||||
UE_LOG(LogMosisPhoneTexture, Log, TEXT("UpdateFromHardwareBuffer: %dx%d"), Desc.width, Desc.height);
|
UE_LOG(LogMosisPhoneTexture, Log, TEXT("ImportHardwareBuffer: %dx%d, format=%d, usage=0x%x"),
|
||||||
|
Desc.width, Desc.height, Desc.format, Desc.usage);
|
||||||
|
|
||||||
// Lock buffer and read data
|
// Verify buffer has GPU sampled image usage (required for Vulkan import)
|
||||||
void* LockedData = nullptr;
|
if ((Desc.usage & AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE) == 0)
|
||||||
int32 Result = AHardwareBuffer_lock(Buffer, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, -1, nullptr, &LockedData);
|
|
||||||
|
|
||||||
if (Result != 0 || !LockedData)
|
|
||||||
{
|
{
|
||||||
UE_LOG(LogMosisPhoneTexture, Error, TEXT("UpdateFromHardwareBuffer: Failed to lock buffer, result=%d"), Result);
|
UE_LOG(LogMosisPhoneTexture, Error,
|
||||||
|
TEXT("ImportHardwareBuffer: Buffer missing AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE flag"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8* PixelData = static_cast<uint8*>(LockedData);
|
// Check if RHI is Vulkan
|
||||||
|
if (GDynamicRHI->GetInterfaceType() != ERHIInterfaceType::Vulkan)
|
||||||
// Copy data to a buffer for update (accounting for stride)
|
|
||||||
uint32 StridePixels = Desc.stride > 0 ? Desc.stride : Desc.width;
|
|
||||||
TArray<uint8> TextureData;
|
|
||||||
TextureData.SetNumUninitialized(Desc.width * Desc.height * 4);
|
|
||||||
|
|
||||||
for (uint32 y = 0; y < Desc.height; y++)
|
|
||||||
{
|
{
|
||||||
uint32 SrcRow = y;
|
UE_LOG(LogMosisPhoneTexture, Error,
|
||||||
uint32 DstRow = Desc.height - 1 - y; // Flip Y for Vulkan
|
TEXT("ImportHardwareBuffer: Vulkan RHI required for hardware buffer import"));
|
||||||
FMemory::Memcpy(
|
return;
|
||||||
TextureData.GetData() + DstRow * Desc.width * 4,
|
|
||||||
PixelData + SrcRow * StridePixels * 4,
|
|
||||||
Desc.width * 4
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AHardwareBuffer_unlock(Buffer, nullptr);
|
// Release old imported texture if any
|
||||||
|
if (ImportedTextureRHI.IsValid())
|
||||||
// Update the texture using UTexture2DDynamic's update regions
|
|
||||||
FTexture2DDynamicResource* TextureResource = static_cast<FTexture2DDynamicResource*>(GetResource());
|
|
||||||
if (TextureResource)
|
|
||||||
{
|
{
|
||||||
ENQUEUE_RENDER_COMMAND(UpdateMosisTexture)(
|
ImportedTextureRHI.SafeRelease();
|
||||||
[TextureResource, Data = MoveTemp(TextureData), Width = Desc.width, Height = Desc.height](FRHICommandListImmediate& RHICmdList)
|
|
||||||
{
|
|
||||||
FUpdateTextureRegion2D Region(0, 0, 0, 0, Width, Height);
|
|
||||||
RHIUpdateTexture2D(
|
|
||||||
TextureResource->GetTexture2DRHI(),
|
|
||||||
0,
|
|
||||||
Region,
|
|
||||||
Width * 4,
|
|
||||||
Data.GetData()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CurrentBuffer = Buffer;
|
||||||
|
|
||||||
|
// Import the hardware buffer using UE5's Vulkan RHI API
|
||||||
|
// This creates a VkImage backed by the AHardwareBuffer's shared memory (zero-copy)
|
||||||
|
IVulkanDynamicRHI* VulkanRHI = GetIVulkanDynamicRHI();
|
||||||
|
ImportedTextureRHI = VulkanRHI->RHICreateTexture2DFromAndroidHardwareBuffer(Buffer);
|
||||||
|
|
||||||
|
if (ImportedTextureRHI.IsValid())
|
||||||
|
{
|
||||||
|
UE_LOG(LogMosisPhoneTexture, Log,
|
||||||
|
TEXT("ImportHardwareBuffer: Successfully imported as Vulkan texture"));
|
||||||
|
|
||||||
|
// Initialize our destination texture if not done yet
|
||||||
|
if (!bIsReady)
|
||||||
|
{
|
||||||
|
Initialize(Desc.width, Desc.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogMosisPhoneTexture, Error,
|
||||||
|
TEXT("ImportHardwareBuffer: Failed to create Vulkan texture from hardware buffer"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UMosisPhoneTexture::NotifyFrameAvailable()
|
void UMosisPhoneTexture::CopyFromImportedBuffer()
|
||||||
{
|
{
|
||||||
if (!CurrentBuffer || !bIsReady)
|
if (!ImportedTextureRHI.IsValid() || !bIsReady)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get buffer dimensions
|
// Get our destination texture resource
|
||||||
AHardwareBuffer_Desc Desc{};
|
|
||||||
AHardwareBuffer_describe(CurrentBuffer, &Desc);
|
|
||||||
|
|
||||||
// Lock buffer and read data
|
|
||||||
void* LockedData = nullptr;
|
|
||||||
int32 Result = AHardwareBuffer_lock(CurrentBuffer, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, -1, nullptr, &LockedData);
|
|
||||||
|
|
||||||
if (Result != 0 || !LockedData)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8* PixelData = static_cast<uint8*>(LockedData);
|
|
||||||
|
|
||||||
// Copy data to a buffer for update (accounting for stride)
|
|
||||||
uint32 StridePixels = Desc.stride > 0 ? Desc.stride : Desc.width;
|
|
||||||
TArray<uint8> TextureData;
|
|
||||||
TextureData.SetNumUninitialized(Desc.width * Desc.height * 4);
|
|
||||||
|
|
||||||
for (uint32 y = 0; y < Desc.height; y++)
|
|
||||||
{
|
|
||||||
uint32 SrcRow = y;
|
|
||||||
uint32 DstRow = Desc.height - 1 - y; // Flip Y for Vulkan
|
|
||||||
FMemory::Memcpy(
|
|
||||||
TextureData.GetData() + DstRow * Desc.width * 4,
|
|
||||||
PixelData + SrcRow * StridePixels * 4,
|
|
||||||
Desc.width * 4
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
AHardwareBuffer_unlock(CurrentBuffer, nullptr);
|
|
||||||
|
|
||||||
// Update the texture
|
|
||||||
FTexture2DDynamicResource* TextureResource = static_cast<FTexture2DDynamicResource*>(GetResource());
|
FTexture2DDynamicResource* TextureResource = static_cast<FTexture2DDynamicResource*>(GetResource());
|
||||||
if (TextureResource)
|
if (!TextureResource)
|
||||||
{
|
{
|
||||||
ENQUEUE_RENDER_COMMAND(UpdateMosisTextureFrame)(
|
return;
|
||||||
[TextureResource, Data = MoveTemp(TextureData), Width = Desc.width, Height = Desc.height](FRHICommandListImmediate& RHICmdList)
|
}
|
||||||
|
|
||||||
|
FTextureRHIRef DestTextureRHI = TextureResource->GetTexture2DRHI();
|
||||||
|
if (!DestTextureRHI.IsValid())
|
||||||
{
|
{
|
||||||
FUpdateTextureRegion2D Region(0, 0, 0, 0, Width, Height);
|
return;
|
||||||
RHIUpdateTexture2D(
|
}
|
||||||
TextureResource->GetTexture2DRHI(),
|
|
||||||
0,
|
// Capture references for the render thread lambda
|
||||||
Region,
|
FTextureRHIRef SrcTexture = ImportedTextureRHI;
|
||||||
Width * 4,
|
FTextureRHIRef DstTexture = DestTextureRHI;
|
||||||
Data.GetData()
|
uint32 Width = TextureWidth;
|
||||||
);
|
uint32 Height = TextureHeight;
|
||||||
|
|
||||||
|
// Enqueue GPU-to-GPU copy on render thread
|
||||||
|
ENQUEUE_RENDER_COMMAND(CopyMosisTexture)(
|
||||||
|
[SrcTexture, DstTexture, Width, Height](FRHICommandListImmediate& RHICmdList)
|
||||||
|
{
|
||||||
|
// Transition source to copy source state
|
||||||
|
RHICmdList.Transition(FRHITransitionInfo(SrcTexture, ERHIAccess::Unknown, ERHIAccess::CopySrc));
|
||||||
|
|
||||||
|
// Transition destination to copy dest state
|
||||||
|
RHICmdList.Transition(FRHITransitionInfo(DstTexture, ERHIAccess::Unknown, ERHIAccess::CopyDest));
|
||||||
|
|
||||||
|
// Perform GPU copy
|
||||||
|
FRHICopyTextureInfo CopyInfo;
|
||||||
|
CopyInfo.Size.X = Width;
|
||||||
|
CopyInfo.Size.Y = Height;
|
||||||
|
CopyInfo.Size.Z = 1;
|
||||||
|
CopyInfo.NumMips = 1;
|
||||||
|
CopyInfo.NumSlices = 1;
|
||||||
|
|
||||||
|
RHICmdList.CopyTexture(SrcTexture, DstTexture, CopyInfo);
|
||||||
|
|
||||||
|
// Transition destination back to shader resource for rendering
|
||||||
|
RHICmdList.Transition(FRHITransitionInfo(DstTexture, ERHIAccess::CopyDest, ERHIAccess::SRVMask));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // PLATFORM_ANDROID
|
#endif // PLATFORM_ANDROID
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
* UMosisPhoneTexture - Dynamic texture that displays the Mosis phone screen.
|
* UMosisPhoneTexture - Dynamic texture that displays the Mosis phone screen.
|
||||||
*
|
*
|
||||||
* This texture is updated from an AHardwareBuffer received from MosisService.
|
* This texture is updated from an AHardwareBuffer received from MosisService.
|
||||||
|
* Uses GPU-to-GPU copy via Vulkan external memory import for optimal performance.
|
||||||
* Inherits from UTexture2DDynamic for proper material integration.
|
* Inherits from UTexture2DDynamic for proper material integration.
|
||||||
*/
|
*/
|
||||||
UCLASS()
|
UCLASS()
|
||||||
@@ -23,26 +24,27 @@ class UMosisPhoneTexture : public UTexture2DDynamic
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
UMosisPhoneTexture();
|
UMosisPhoneTexture();
|
||||||
|
virtual ~UMosisPhoneTexture();
|
||||||
|
|
||||||
/** Initialize the texture with dimensions */
|
/** Initialize the texture with dimensions */
|
||||||
void Initialize(uint32 InWidth, uint32 InHeight);
|
void Initialize(uint32 InWidth, uint32 InHeight);
|
||||||
|
|
||||||
#if PLATFORM_ANDROID
|
#if PLATFORM_ANDROID
|
||||||
/**
|
/**
|
||||||
* Update the texture from a hardware buffer.
|
* Import a hardware buffer for GPU-accelerated texture updates.
|
||||||
|
* Creates a Vulkan image from the AHardwareBuffer for zero-copy import.
|
||||||
* @param Buffer The AHardwareBuffer from MosisService
|
* @param Buffer The AHardwareBuffer from MosisService
|
||||||
*/
|
*/
|
||||||
void UpdateFromHardwareBuffer(AHardwareBuffer* Buffer);
|
void ImportHardwareBuffer(AHardwareBuffer* Buffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify that a new frame is available.
|
* Perform GPU-to-GPU copy from imported buffer to this texture.
|
||||||
* Copies buffer data to the texture.
|
* Called each frame when new content is available.
|
||||||
*/
|
*/
|
||||||
void NotifyFrameAvailable();
|
void CopyFromImportedBuffer();
|
||||||
|
|
||||||
/** Store the current hardware buffer reference */
|
/** Check if the imported buffer is valid */
|
||||||
void SetHardwareBuffer(AHardwareBuffer* Buffer) { CurrentBuffer = Buffer; }
|
bool HasImportedBuffer() const { return ImportedTextureRHI.IsValid(); }
|
||||||
AHardwareBuffer* GetHardwareBuffer() const { return CurrentBuffer; }
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** Check if the texture is ready for rendering */
|
/** Check if the texture is ready for rendering */
|
||||||
@@ -50,10 +52,17 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
#if PLATFORM_ANDROID
|
#if PLATFORM_ANDROID
|
||||||
/** Current hardware buffer */
|
/** RHI texture imported from AHardwareBuffer (zero-copy Vulkan import) */
|
||||||
|
FTextureRHIRef ImportedTextureRHI;
|
||||||
|
|
||||||
|
/** Current hardware buffer reference */
|
||||||
AHardwareBuffer* CurrentBuffer = nullptr;
|
AHardwareBuffer* CurrentBuffer = nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** True when texture has been initialized */
|
/** True when texture has been initialized */
|
||||||
bool bIsReady = false;
|
bool bIsReady = false;
|
||||||
|
|
||||||
|
/** Texture dimensions */
|
||||||
|
uint32 TextureWidth = 0;
|
||||||
|
uint32 TextureHeight = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
207
build-unreal.bat
Normal file
207
build-unreal.bat
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
|
||||||
|
:: MosisUnreal Build and Deploy Script
|
||||||
|
:: Usage: build-unreal.bat [build|install|deploy|launch|clean]
|
||||||
|
:: build - Build Android APK only
|
||||||
|
:: install - Install APK to device only
|
||||||
|
:: deploy - Build and install (default)
|
||||||
|
:: launch - Launch app (starts MosisService first)
|
||||||
|
:: clean - Clean build artifacts
|
||||||
|
|
||||||
|
set ENGINE_PATH=D:\Epic\UE_5.5
|
||||||
|
set PROJECT_PATH=%~dp0MosisUnreal.uproject
|
||||||
|
set UAT_PATH=%ENGINE_PATH%\Engine\Build\BatchFiles\RunUAT.bat
|
||||||
|
set CONFIG=Development
|
||||||
|
|
||||||
|
:: Parse command line argument
|
||||||
|
set ACTION=%1
|
||||||
|
if "%ACTION%"=="" set ACTION=deploy
|
||||||
|
|
||||||
|
:: Validate engine path
|
||||||
|
if not exist "%UAT_PATH%" (
|
||||||
|
echo ERROR: Unreal Engine not found at %ENGINE_PATH%
|
||||||
|
echo Please update ENGINE_PATH in this script.
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
:: Execute action
|
||||||
|
if "%ACTION%"=="build" goto :build
|
||||||
|
if "%ACTION%"=="install" goto :install
|
||||||
|
if "%ACTION%"=="deploy" goto :deploy
|
||||||
|
if "%ACTION%"=="launch" goto :launch
|
||||||
|
if "%ACTION%"=="clean" goto :clean
|
||||||
|
|
||||||
|
echo Unknown action: %ACTION%
|
||||||
|
echo Usage: build-unreal.bat [build^|install^|deploy^|launch^|clean]
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:build
|
||||||
|
echo.
|
||||||
|
echo ============================================
|
||||||
|
echo Building MosisUnreal for Android...
|
||||||
|
echo ============================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
call "%UAT_PATH%" BuildCookRun ^
|
||||||
|
-project="%PROJECT_PATH%" ^
|
||||||
|
-platform=Android ^
|
||||||
|
-clientconfig=%CONFIG% ^
|
||||||
|
-build -cook -stage -pak -package ^
|
||||||
|
-noP4 ^
|
||||||
|
-utf8output
|
||||||
|
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo.
|
||||||
|
echo ERROR: Build failed!
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Build completed successfully!
|
||||||
|
echo APK: %~dp0Binaries\Android\MosisUnreal-arm64.apk
|
||||||
|
goto :eof
|
||||||
|
|
||||||
|
:install
|
||||||
|
echo.
|
||||||
|
echo ============================================
|
||||||
|
echo Installing MosisUnreal to device...
|
||||||
|
echo ============================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
:: Check for connected device
|
||||||
|
adb devices | findstr /r /c:"device$" >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo ERROR: No Android device connected!
|
||||||
|
echo Connect a device and enable USB debugging.
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
:: Get device ID
|
||||||
|
for /f "tokens=1" %%d in ('adb devices ^| findstr /r /c:"device$"') do (
|
||||||
|
set DEVICE=%%d
|
||||||
|
goto :found_device
|
||||||
|
)
|
||||||
|
:found_device
|
||||||
|
echo Device: %DEVICE%
|
||||||
|
|
||||||
|
set APK_PATH=%~dp0Binaries\Android\MosisUnreal-arm64.apk
|
||||||
|
set OBB_PATH=%~dp0Binaries\Android\main.1.com.omixlab.MosisUnreal.obb
|
||||||
|
|
||||||
|
:: Check APK exists
|
||||||
|
if not exist "%APK_PATH%" (
|
||||||
|
echo ERROR: APK not found at %APK_PATH%
|
||||||
|
echo Run 'build-unreal.bat build' first.
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
:: Install APK
|
||||||
|
echo Installing APK...
|
||||||
|
adb -s %DEVICE% install -r "%APK_PATH%"
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo ERROR: APK installation failed!
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
:: Install OBB if exists
|
||||||
|
if exist "%OBB_PATH%" (
|
||||||
|
echo.
|
||||||
|
echo Installing OBB...
|
||||||
|
|
||||||
|
:: Create temp directory
|
||||||
|
adb -s %DEVICE% shell "mkdir -p /data/local/tmp/obb/com.omixlab.MosisUnreal"
|
||||||
|
|
||||||
|
:: Push OBB (use forward slashes for adb)
|
||||||
|
set OBB_UNIX=%OBB_PATH:\=/%
|
||||||
|
adb -s %DEVICE% push "!OBB_UNIX!" /data/local/tmp/obb/com.omixlab.MosisUnreal/
|
||||||
|
|
||||||
|
:: Move to final location
|
||||||
|
adb -s %DEVICE% shell "rm -rf /sdcard/Android/obb/com.omixlab.MosisUnreal"
|
||||||
|
adb -s %DEVICE% shell "mv /data/local/tmp/obb/com.omixlab.MosisUnreal /sdcard/Android/obb/"
|
||||||
|
|
||||||
|
echo OBB installed.
|
||||||
|
) else (
|
||||||
|
echo No OBB file found, skipping.
|
||||||
|
)
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Installation completed!
|
||||||
|
echo.
|
||||||
|
echo To launch: adb -s %DEVICE% shell am start -n com.omixlab.MosisUnreal/com.epicgames.unreal.GameActivity
|
||||||
|
goto :eof
|
||||||
|
|
||||||
|
:deploy
|
||||||
|
call :build
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
call :install
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
call :launch
|
||||||
|
goto :eof
|
||||||
|
|
||||||
|
:launch
|
||||||
|
echo.
|
||||||
|
echo ============================================
|
||||||
|
echo Launching MosisUnreal...
|
||||||
|
echo ============================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
:: Check for connected device
|
||||||
|
adb devices | findstr /r /c:"device$" >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo ERROR: No Android device connected!
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
:: Get device ID
|
||||||
|
for /f "tokens=1" %%d in ('adb devices ^| findstr /r /c:"device$"') do (
|
||||||
|
set DEVICE=%%d
|
||||||
|
goto :found_device_launch
|
||||||
|
)
|
||||||
|
:found_device_launch
|
||||||
|
echo Device: %DEVICE%
|
||||||
|
|
||||||
|
:: Stop any running instances
|
||||||
|
echo Stopping existing instances...
|
||||||
|
adb -s %DEVICE% shell am force-stop com.omixlab.MosisUnreal >nul 2>&1
|
||||||
|
adb -s %DEVICE% shell am force-stop com.omixlab.mosis >nul 2>&1
|
||||||
|
timeout /t 1 /nobreak >nul
|
||||||
|
|
||||||
|
:: Start MosisService first
|
||||||
|
echo Starting MosisService...
|
||||||
|
adb -s %DEVICE% shell am start -n com.omixlab.mosis/.MainActivity
|
||||||
|
timeout /t 2 /nobreak >nul
|
||||||
|
|
||||||
|
:: Start MosisUnreal
|
||||||
|
echo Starting MosisUnreal...
|
||||||
|
adb -s %DEVICE% shell am start -n com.omixlab.MosisUnreal/com.epicgames.unreal.GameActivity
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Launched! Monitoring logs (Ctrl+C to stop)...
|
||||||
|
echo.
|
||||||
|
adb -s %DEVICE% logcat -s MosisSDK MosisOS MosisTest UE
|
||||||
|
goto :eof
|
||||||
|
|
||||||
|
:clean
|
||||||
|
echo.
|
||||||
|
echo ============================================
|
||||||
|
echo Cleaning build artifacts...
|
||||||
|
echo ============================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
if exist "%~dp0Binaries" (
|
||||||
|
echo Removing Binaries...
|
||||||
|
rmdir /s /q "%~dp0Binaries"
|
||||||
|
)
|
||||||
|
|
||||||
|
if exist "%~dp0Intermediate\Build" (
|
||||||
|
echo Removing Intermediate\Build...
|
||||||
|
rmdir /s /q "%~dp0Intermediate\Build"
|
||||||
|
)
|
||||||
|
|
||||||
|
if exist "%~dp0Saved\StagedBuilds" (
|
||||||
|
echo Removing Saved\StagedBuilds...
|
||||||
|
rmdir /s /q "%~dp0Saved\StagedBuilds"
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Clean completed!
|
||||||
|
goto :eof
|
||||||
Reference in New Issue
Block a user