add phone screen material and fix asset cooking
This commit is contained in:
@@ -2,3 +2,6 @@
|
|||||||
ProjectID=DD810CA045CA9288C9C53E8638A45978
|
ProjectID=DD810CA045CA9288C9C53E8638A45978
|
||||||
bStartInVR=True
|
bStartInVR=True
|
||||||
|
|
||||||
|
[/Script/UnrealEd.ProjectPackagingSettings]
|
||||||
|
+DirectoriesToAlwaysCook=(Path="/Game/Mosis")
|
||||||
|
|
||||||
|
|||||||
BIN
Content/Mosis/Materials/M_PhoneScreen.uasset
LFS
Normal file
BIN
Content/Mosis/Materials/M_PhoneScreen.uasset
LFS
Normal file
Binary file not shown.
@@ -89,7 +89,6 @@ Plugins/MosisSDK/
|
|||||||
│ ├── MosisClient.h/cpp # AIDL client implementation
|
│ ├── MosisClient.h/cpp # AIDL client implementation
|
||||||
│ ├── MosisPhoneTexture.h/cpp # UTexture for phone screen
|
│ ├── MosisPhoneTexture.h/cpp # UTexture for phone screen
|
||||||
│ ├── MosisPhoneTextureResource.h/cpp # Render thread resource
|
│ ├── MosisPhoneTextureResource.h/cpp # Render thread resource
|
||||||
│ ├── MosisVulkanTexture.h/cpp # Legacy Vulkan import (unused)
|
|
||||||
│ ├── MosisPhoneComponent.cpp
|
│ ├── MosisPhoneComponent.cpp
|
||||||
│ ├── MosisPhoneActor.cpp
|
│ ├── MosisPhoneActor.cpp
|
||||||
│ └── Android/
|
│ └── Android/
|
||||||
|
|||||||
@@ -25,6 +25,14 @@ AMosisPhoneActor::AMosisPhoneActor()
|
|||||||
PhoneMesh->SetStaticMesh(PlaneMeshFinder.Object);
|
PhoneMesh->SetStaticMesh(PlaneMeshFinder.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load the phone screen material
|
||||||
|
static ConstructorHelpers::FObjectFinder<UMaterialInterface> PhoneMaterialFinder(
|
||||||
|
TEXT("/Game/Mosis/Materials/M_PhoneScreen.M_PhoneScreen"));
|
||||||
|
if (PhoneMaterialFinder.Succeeded())
|
||||||
|
{
|
||||||
|
BaseMaterial = PhoneMaterialFinder.Object;
|
||||||
|
}
|
||||||
|
|
||||||
// Create phone component
|
// Create phone component
|
||||||
PhoneComponent = CreateDefaultSubobject<UMosisPhoneComponent>(TEXT("PhoneComponent"));
|
PhoneComponent = CreateDefaultSubobject<UMosisPhoneComponent>(TEXT("PhoneComponent"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,421 +0,0 @@
|
|||||||
#include "MosisVulkanTexture.h"
|
|
||||||
|
|
||||||
#if PLATFORM_ANDROID
|
|
||||||
|
|
||||||
#include "Logging/LogMacros.h"
|
|
||||||
|
|
||||||
DEFINE_LOG_CATEGORY_STATIC(LogMosisVulkan, Log, All);
|
|
||||||
|
|
||||||
MosisVulkanTexture::~MosisVulkanTexture()
|
|
||||||
{
|
|
||||||
Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MosisVulkanTexture::Initialize(VkInstance instance, VkPhysicalDevice physDevice, VkDevice device, uint32_t queueFamilyIndex)
|
|
||||||
{
|
|
||||||
if (m_Initialized)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Instance = instance;
|
|
||||||
m_PhysicalDevice = physDevice;
|
|
||||||
m_Device = device;
|
|
||||||
m_QueueFamilyIndex = queueFamilyIndex;
|
|
||||||
|
|
||||||
// Load extension function
|
|
||||||
vkGetAndroidHardwareBufferPropertiesANDROID =
|
|
||||||
reinterpret_cast<PFN_vkGetAndroidHardwareBufferPropertiesANDROID>(
|
|
||||||
vkGetInstanceProcAddr(m_Instance, "vkGetAndroidHardwareBufferPropertiesANDROID")
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!vkGetAndroidHardwareBufferPropertiesANDROID)
|
|
||||||
{
|
|
||||||
UE_LOG(LogMosisVulkan, Error, TEXT("Initialize: vkGetAndroidHardwareBufferPropertiesANDROID not available"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Initialized = true;
|
|
||||||
UE_LOG(LogMosisVulkan, Log, TEXT("Initialize: Success"));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MosisVulkanTexture::Create(AHardwareBuffer* buffer)
|
|
||||||
{
|
|
||||||
if (!m_Initialized)
|
|
||||||
{
|
|
||||||
UE_LOG(LogMosisVulkan, Error, TEXT("Create: Not initialized"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_Created)
|
|
||||||
{
|
|
||||||
Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get buffer dimensions
|
|
||||||
AHardwareBuffer_Desc desc{};
|
|
||||||
AHardwareBuffer_describe(buffer, &desc);
|
|
||||||
m_Width = desc.width;
|
|
||||||
m_Height = desc.height;
|
|
||||||
|
|
||||||
UE_LOG(LogMosisVulkan, Log, TEXT("Create: %dx%d texture"), m_Width, m_Height);
|
|
||||||
|
|
||||||
if (!ImportHardwareBuffer(buffer))
|
|
||||||
{
|
|
||||||
UE_LOG(LogMosisVulkan, Error, TEXT("Create: Failed to import hardware buffer"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!CreateLocalImage())
|
|
||||||
{
|
|
||||||
UE_LOG(LogMosisVulkan, Error, TEXT("Create: Failed to create local image"));
|
|
||||||
DestroyImportedResources();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Created = true;
|
|
||||||
UE_LOG(LogMosisVulkan, Log, TEXT("Create: Success"));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MosisVulkanTexture::ImportHardwareBuffer(AHardwareBuffer* buffer)
|
|
||||||
{
|
|
||||||
// Query hardware buffer properties
|
|
||||||
VkAndroidHardwareBufferFormatPropertiesANDROID formatProps{};
|
|
||||||
formatProps.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID;
|
|
||||||
|
|
||||||
VkAndroidHardwareBufferPropertiesANDROID props{};
|
|
||||||
props.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID;
|
|
||||||
props.pNext = &formatProps;
|
|
||||||
|
|
||||||
if (vkGetAndroidHardwareBufferPropertiesANDROID(m_Device, buffer, &props) != VK_SUCCESS)
|
|
||||||
{
|
|
||||||
UE_LOG(LogMosisVulkan, Error, TEXT("ImportHardwareBuffer: Failed to get properties"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Format = formatProps.format;
|
|
||||||
UE_LOG(LogMosisVulkan, Log, TEXT("ImportHardwareBuffer: Format=%d"), static_cast<int>(m_Format));
|
|
||||||
|
|
||||||
// Create VkImage with external memory
|
|
||||||
VkExternalMemoryImageCreateInfo extMemImageInfo{};
|
|
||||||
extMemImageInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;
|
|
||||||
extMemImageInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID;
|
|
||||||
|
|
||||||
VkImageCreateInfo imageInfo{};
|
|
||||||
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
|
||||||
imageInfo.pNext = &extMemImageInfo;
|
|
||||||
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
|
||||||
imageInfo.format = m_Format;
|
|
||||||
imageInfo.extent = {m_Width, m_Height, 1};
|
|
||||||
imageInfo.mipLevels = 1;
|
|
||||||
imageInfo.arrayLayers = 1;
|
|
||||||
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
||||||
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
||||||
imageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
|
||||||
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
||||||
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
||||||
|
|
||||||
if (vkCreateImage(m_Device, &imageInfo, nullptr, &m_ImportedImage) != VK_SUCCESS)
|
|
||||||
{
|
|
||||||
UE_LOG(LogMosisVulkan, Error, TEXT("ImportHardwareBuffer: Failed to create image"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Import memory from hardware buffer
|
|
||||||
VkImportAndroidHardwareBufferInfoANDROID importInfo{};
|
|
||||||
importInfo.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID;
|
|
||||||
importInfo.buffer = buffer;
|
|
||||||
|
|
||||||
VkMemoryDedicatedAllocateInfo dedicatedInfo{};
|
|
||||||
dedicatedInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO;
|
|
||||||
dedicatedInfo.pNext = &importInfo;
|
|
||||||
dedicatedInfo.image = m_ImportedImage;
|
|
||||||
|
|
||||||
VkMemoryAllocateInfo allocInfo{};
|
|
||||||
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
|
||||||
allocInfo.pNext = &dedicatedInfo;
|
|
||||||
allocInfo.allocationSize = props.allocationSize;
|
|
||||||
allocInfo.memoryTypeIndex = FindMemoryType(props.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
|
||||||
|
|
||||||
if (vkAllocateMemory(m_Device, &allocInfo, nullptr, &m_ImportedMemory) != VK_SUCCESS)
|
|
||||||
{
|
|
||||||
UE_LOG(LogMosisVulkan, Error, TEXT("ImportHardwareBuffer: Failed to allocate memory"));
|
|
||||||
vkDestroyImage(m_Device, m_ImportedImage, nullptr);
|
|
||||||
m_ImportedImage = VK_NULL_HANDLE;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vkBindImageMemory(m_Device, m_ImportedImage, m_ImportedMemory, 0) != VK_SUCCESS)
|
|
||||||
{
|
|
||||||
UE_LOG(LogMosisVulkan, Error, TEXT("ImportHardwareBuffer: Failed to bind memory"));
|
|
||||||
DestroyImportedResources();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
UE_LOG(LogMosisVulkan, Log, TEXT("ImportHardwareBuffer: Success"));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MosisVulkanTexture::CreateLocalImage()
|
|
||||||
{
|
|
||||||
// Create local VkImage
|
|
||||||
VkImageCreateInfo imageInfo{};
|
|
||||||
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
|
||||||
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
|
||||||
imageInfo.format = m_Format;
|
|
||||||
imageInfo.extent = {m_Width, m_Height, 1};
|
|
||||||
imageInfo.mipLevels = 1;
|
|
||||||
imageInfo.arrayLayers = 1;
|
|
||||||
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
||||||
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
||||||
imageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
|
||||||
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
||||||
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
||||||
|
|
||||||
if (vkCreateImage(m_Device, &imageInfo, nullptr, &m_LocalImage) != VK_SUCCESS)
|
|
||||||
{
|
|
||||||
UE_LOG(LogMosisVulkan, Error, TEXT("CreateLocalImage: Failed to create image"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate memory
|
|
||||||
VkMemoryRequirements memReqs;
|
|
||||||
vkGetImageMemoryRequirements(m_Device, m_LocalImage, &memReqs);
|
|
||||||
|
|
||||||
VkMemoryAllocateInfo allocInfo{};
|
|
||||||
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
|
||||||
allocInfo.allocationSize = memReqs.size;
|
|
||||||
allocInfo.memoryTypeIndex = FindMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
|
||||||
|
|
||||||
if (vkAllocateMemory(m_Device, &allocInfo, nullptr, &m_LocalMemory) != VK_SUCCESS)
|
|
||||||
{
|
|
||||||
UE_LOG(LogMosisVulkan, Error, TEXT("CreateLocalImage: Failed to allocate memory"));
|
|
||||||
vkDestroyImage(m_Device, m_LocalImage, nullptr);
|
|
||||||
m_LocalImage = VK_NULL_HANDLE;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vkBindImageMemory(m_Device, m_LocalImage, m_LocalMemory, 0) != VK_SUCCESS)
|
|
||||||
{
|
|
||||||
UE_LOG(LogMosisVulkan, Error, TEXT("CreateLocalImage: Failed to bind memory"));
|
|
||||||
DestroyLocalResources();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create image view
|
|
||||||
VkImageViewCreateInfo viewInfo{};
|
|
||||||
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
||||||
viewInfo.image = m_LocalImage;
|
|
||||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
||||||
viewInfo.format = m_Format;
|
|
||||||
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
||||||
viewInfo.subresourceRange.baseMipLevel = 0;
|
|
||||||
viewInfo.subresourceRange.levelCount = 1;
|
|
||||||
viewInfo.subresourceRange.baseArrayLayer = 0;
|
|
||||||
viewInfo.subresourceRange.layerCount = 1;
|
|
||||||
|
|
||||||
if (vkCreateImageView(m_Device, &viewInfo, nullptr, &m_LocalImageView) != VK_SUCCESS)
|
|
||||||
{
|
|
||||||
UE_LOG(LogMosisVulkan, Error, TEXT("CreateLocalImage: Failed to create image view"));
|
|
||||||
DestroyLocalResources();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create sampler
|
|
||||||
VkSamplerCreateInfo samplerInfo{};
|
|
||||||
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
|
||||||
samplerInfo.magFilter = VK_FILTER_LINEAR;
|
|
||||||
samplerInfo.minFilter = VK_FILTER_LINEAR;
|
|
||||||
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
||||||
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
||||||
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
||||||
samplerInfo.anisotropyEnable = VK_FALSE;
|
|
||||||
samplerInfo.maxAnisotropy = 1.0f;
|
|
||||||
samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
|
|
||||||
samplerInfo.unnormalizedCoordinates = VK_FALSE;
|
|
||||||
samplerInfo.compareEnable = VK_FALSE;
|
|
||||||
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
|
||||||
|
|
||||||
if (vkCreateSampler(m_Device, &samplerInfo, nullptr, &m_Sampler) != VK_SUCCESS)
|
|
||||||
{
|
|
||||||
UE_LOG(LogMosisVulkan, Error, TEXT("CreateLocalImage: Failed to create sampler"));
|
|
||||||
DestroyLocalResources();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
UE_LOG(LogMosisVulkan, Log, TEXT("CreateLocalImage: Success"));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MosisVulkanTexture::CopyToLocal(VkCommandBuffer cmd)
|
|
||||||
{
|
|
||||||
if (!m_Created)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transition imported image to transfer src
|
|
||||||
VkImageMemoryBarrier srcBarrier{};
|
|
||||||
srcBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
||||||
srcBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
||||||
srcBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
|
||||||
srcBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
||||||
srcBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
||||||
srcBarrier.image = m_ImportedImage;
|
|
||||||
srcBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
||||||
srcBarrier.subresourceRange.baseMipLevel = 0;
|
|
||||||
srcBarrier.subresourceRange.levelCount = 1;
|
|
||||||
srcBarrier.subresourceRange.baseArrayLayer = 0;
|
|
||||||
srcBarrier.subresourceRange.layerCount = 1;
|
|
||||||
srcBarrier.srcAccessMask = 0;
|
|
||||||
srcBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
|
||||||
|
|
||||||
vkCmdPipelineBarrier(
|
|
||||||
cmd,
|
|
||||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
|
||||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
||||||
0, 0, nullptr, 0, nullptr, 1, &srcBarrier
|
|
||||||
);
|
|
||||||
|
|
||||||
// Transition local image to transfer dst
|
|
||||||
VkImageMemoryBarrier dstBarrier{};
|
|
||||||
dstBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
||||||
dstBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
||||||
dstBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
||||||
dstBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
||||||
dstBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
||||||
dstBarrier.image = m_LocalImage;
|
|
||||||
dstBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
||||||
dstBarrier.subresourceRange.baseMipLevel = 0;
|
|
||||||
dstBarrier.subresourceRange.levelCount = 1;
|
|
||||||
dstBarrier.subresourceRange.baseArrayLayer = 0;
|
|
||||||
dstBarrier.subresourceRange.layerCount = 1;
|
|
||||||
dstBarrier.srcAccessMask = 0;
|
|
||||||
dstBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
||||||
|
|
||||||
vkCmdPipelineBarrier(
|
|
||||||
cmd,
|
|
||||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
|
||||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
||||||
0, 0, nullptr, 0, nullptr, 1, &dstBarrier
|
|
||||||
);
|
|
||||||
|
|
||||||
// Copy image
|
|
||||||
VkImageCopy copyRegion{};
|
|
||||||
copyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
||||||
copyRegion.srcSubresource.layerCount = 1;
|
|
||||||
copyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
||||||
copyRegion.dstSubresource.layerCount = 1;
|
|
||||||
copyRegion.extent = {m_Width, m_Height, 1};
|
|
||||||
|
|
||||||
vkCmdCopyImage(
|
|
||||||
cmd,
|
|
||||||
m_ImportedImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
||||||
m_LocalImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
||||||
1, ©Region
|
|
||||||
);
|
|
||||||
|
|
||||||
// Transition local image to shader read
|
|
||||||
VkImageMemoryBarrier shaderBarrier{};
|
|
||||||
shaderBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
||||||
shaderBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
||||||
shaderBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
||||||
shaderBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
||||||
shaderBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
||||||
shaderBarrier.image = m_LocalImage;
|
|
||||||
shaderBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
||||||
shaderBarrier.subresourceRange.baseMipLevel = 0;
|
|
||||||
shaderBarrier.subresourceRange.levelCount = 1;
|
|
||||||
shaderBarrier.subresourceRange.baseArrayLayer = 0;
|
|
||||||
shaderBarrier.subresourceRange.layerCount = 1;
|
|
||||||
shaderBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
||||||
shaderBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
||||||
|
|
||||||
vkCmdPipelineBarrier(
|
|
||||||
cmd,
|
|
||||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
||||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
|
||||||
0, 0, nullptr, 0, nullptr, 1, &shaderBarrier
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t MosisVulkanTexture::FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties)
|
|
||||||
{
|
|
||||||
VkPhysicalDeviceMemoryProperties memProps;
|
|
||||||
vkGetPhysicalDeviceMemoryProperties(m_PhysicalDevice, &memProps);
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < memProps.memoryTypeCount; i++)
|
|
||||||
{
|
|
||||||
if ((typeFilter & (1 << i)) &&
|
|
||||||
(memProps.memoryTypes[i].propertyFlags & properties) == properties)
|
|
||||||
{
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UE_LOG(LogMosisVulkan, Error, TEXT("FindMemoryType: Failed to find suitable memory type"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MosisVulkanTexture::Destroy()
|
|
||||||
{
|
|
||||||
if (m_Device && m_Created)
|
|
||||||
{
|
|
||||||
vkDeviceWaitIdle(m_Device);
|
|
||||||
}
|
|
||||||
|
|
||||||
DestroyLocalResources();
|
|
||||||
DestroyImportedResources();
|
|
||||||
|
|
||||||
m_Width = 0;
|
|
||||||
m_Height = 0;
|
|
||||||
m_Created = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MosisVulkanTexture::DestroyImportedResources()
|
|
||||||
{
|
|
||||||
if (m_Device)
|
|
||||||
{
|
|
||||||
if (m_ImportedMemory != VK_NULL_HANDLE)
|
|
||||||
{
|
|
||||||
vkFreeMemory(m_Device, m_ImportedMemory, nullptr);
|
|
||||||
m_ImportedMemory = VK_NULL_HANDLE;
|
|
||||||
}
|
|
||||||
if (m_ImportedImage != VK_NULL_HANDLE)
|
|
||||||
{
|
|
||||||
vkDestroyImage(m_Device, m_ImportedImage, nullptr);
|
|
||||||
m_ImportedImage = VK_NULL_HANDLE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MosisVulkanTexture::DestroyLocalResources()
|
|
||||||
{
|
|
||||||
if (m_Device)
|
|
||||||
{
|
|
||||||
if (m_Sampler != VK_NULL_HANDLE)
|
|
||||||
{
|
|
||||||
vkDestroySampler(m_Device, m_Sampler, nullptr);
|
|
||||||
m_Sampler = VK_NULL_HANDLE;
|
|
||||||
}
|
|
||||||
if (m_LocalImageView != VK_NULL_HANDLE)
|
|
||||||
{
|
|
||||||
vkDestroyImageView(m_Device, m_LocalImageView, nullptr);
|
|
||||||
m_LocalImageView = VK_NULL_HANDLE;
|
|
||||||
}
|
|
||||||
if (m_LocalMemory != VK_NULL_HANDLE)
|
|
||||||
{
|
|
||||||
vkFreeMemory(m_Device, m_LocalMemory, nullptr);
|
|
||||||
m_LocalMemory = VK_NULL_HANDLE;
|
|
||||||
}
|
|
||||||
if (m_LocalImage != VK_NULL_HANDLE)
|
|
||||||
{
|
|
||||||
vkDestroyImage(m_Device, m_LocalImage, nullptr);
|
|
||||||
m_LocalImage = VK_NULL_HANDLE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // PLATFORM_ANDROID
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#if PLATFORM_ANDROID
|
|
||||||
|
|
||||||
#include "CoreMinimal.h"
|
|
||||||
#include <vulkan/vulkan.h>
|
|
||||||
#include <vulkan/vulkan_android.h>
|
|
||||||
#include <android/hardware_buffer.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MosisVulkanTexture - Imports AHardwareBuffer as Vulkan texture for Unreal.
|
|
||||||
* Creates both imported and local copy images for safe rendering.
|
|
||||||
*/
|
|
||||||
class MosisVulkanTexture
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MosisVulkanTexture() = default;
|
|
||||||
~MosisVulkanTexture();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize with Vulkan device objects.
|
|
||||||
* Must be called before Create().
|
|
||||||
*/
|
|
||||||
bool Initialize(VkInstance instance, VkPhysicalDevice physDevice, VkDevice device, uint32_t queueFamilyIndex);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create texture from a hardware buffer.
|
|
||||||
* @param buffer The AHardwareBuffer from the Mosis service
|
|
||||||
* @return true if creation succeeded
|
|
||||||
*/
|
|
||||||
bool Create(AHardwareBuffer* buffer);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copy from imported image to local image.
|
|
||||||
* Call this each frame when a new frame is available.
|
|
||||||
*/
|
|
||||||
void CopyToLocal(VkCommandBuffer cmd);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy all resources.
|
|
||||||
*/
|
|
||||||
void Destroy();
|
|
||||||
|
|
||||||
// Accessors
|
|
||||||
VkImage GetLocalImage() const { return m_LocalImage; }
|
|
||||||
VkImageView GetLocalImageView() const { return m_LocalImageView; }
|
|
||||||
VkFormat GetFormat() const { return m_Format; }
|
|
||||||
uint32_t GetWidth() const { return m_Width; }
|
|
||||||
uint32_t GetHeight() const { return m_Height; }
|
|
||||||
bool IsValid() const { return m_Created; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool ImportHardwareBuffer(AHardwareBuffer* buffer);
|
|
||||||
bool CreateLocalImage();
|
|
||||||
void DestroyImportedResources();
|
|
||||||
void DestroyLocalResources();
|
|
||||||
uint32_t FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties);
|
|
||||||
|
|
||||||
// Vulkan objects
|
|
||||||
VkInstance m_Instance = VK_NULL_HANDLE;
|
|
||||||
VkPhysicalDevice m_PhysicalDevice = VK_NULL_HANDLE;
|
|
||||||
VkDevice m_Device = VK_NULL_HANDLE;
|
|
||||||
uint32_t m_QueueFamilyIndex = 0;
|
|
||||||
|
|
||||||
// Imported from HardwareBuffer
|
|
||||||
VkImage m_ImportedImage = VK_NULL_HANDLE;
|
|
||||||
VkDeviceMemory m_ImportedMemory = VK_NULL_HANDLE;
|
|
||||||
|
|
||||||
// Local copy for safe rendering
|
|
||||||
VkImage m_LocalImage = VK_NULL_HANDLE;
|
|
||||||
VkDeviceMemory m_LocalMemory = VK_NULL_HANDLE;
|
|
||||||
VkImageView m_LocalImageView = VK_NULL_HANDLE;
|
|
||||||
VkSampler m_Sampler = VK_NULL_HANDLE;
|
|
||||||
|
|
||||||
// Format info
|
|
||||||
VkFormat m_Format = VK_FORMAT_R8G8B8A8_UNORM;
|
|
||||||
uint32_t m_Width = 0;
|
|
||||||
uint32_t m_Height = 0;
|
|
||||||
|
|
||||||
bool m_Initialized = false;
|
|
||||||
bool m_Created = false;
|
|
||||||
|
|
||||||
// Extension function pointer
|
|
||||||
PFN_vkGetAndroidHardwareBufferPropertiesANDROID vkGetAndroidHardwareBufferPropertiesANDROID = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // PLATFORM_ANDROID
|
|
||||||
67
Scripts/create_phone_material.py
Normal file
67
Scripts/create_phone_material.py
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
# Unreal Editor Python script to create the Mosis phone screen material
|
||||||
|
# Run this in the Editor: File > Execute Python Script
|
||||||
|
# Or from Python console: exec(open('Scripts/create_phone_material.py').read())
|
||||||
|
|
||||||
|
import unreal
|
||||||
|
|
||||||
|
def create_phone_screen_material():
|
||||||
|
"""Create an unlit emissive material for displaying the phone screen."""
|
||||||
|
|
||||||
|
asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
|
||||||
|
material_factory = unreal.MaterialFactoryNew()
|
||||||
|
|
||||||
|
# Create the material asset
|
||||||
|
material_path = "/Game/Mosis/Materials"
|
||||||
|
material_name = "M_PhoneScreen"
|
||||||
|
|
||||||
|
# Check if material already exists
|
||||||
|
if unreal.EditorAssetLibrary.does_asset_exist(f"{material_path}/{material_name}"):
|
||||||
|
unreal.log_warning(f"Material {material_name} already exists, skipping creation")
|
||||||
|
return unreal.load_asset(f"{material_path}/{material_name}")
|
||||||
|
|
||||||
|
# Create the material
|
||||||
|
material = asset_tools.create_asset(
|
||||||
|
material_name,
|
||||||
|
material_path,
|
||||||
|
unreal.Material,
|
||||||
|
material_factory
|
||||||
|
)
|
||||||
|
|
||||||
|
if not material:
|
||||||
|
unreal.log_error("Failed to create material")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Configure material properties for phone screen display
|
||||||
|
material.set_editor_property("shading_model", unreal.MaterialShadingModel.MSM_UNLIT)
|
||||||
|
material.set_editor_property("blend_mode", unreal.BlendMode.BLEND_OPAQUE)
|
||||||
|
material.set_editor_property("two_sided", False)
|
||||||
|
|
||||||
|
# Get the material editor subsystem to add nodes
|
||||||
|
mel = unreal.MaterialEditingLibrary
|
||||||
|
|
||||||
|
# Create texture parameter node
|
||||||
|
texture_param = mel.create_material_expression(
|
||||||
|
material,
|
||||||
|
unreal.MaterialExpressionTextureSampleParameter2D,
|
||||||
|
-400, 0
|
||||||
|
)
|
||||||
|
texture_param.set_editor_property("parameter_name", "PhoneScreen")
|
||||||
|
|
||||||
|
# Connect texture RGB to emissive color
|
||||||
|
mel.connect_material_property(
|
||||||
|
texture_param, "RGB",
|
||||||
|
unreal.MaterialProperty.MP_EMISSIVE_COLOR
|
||||||
|
)
|
||||||
|
|
||||||
|
# Recompile the material
|
||||||
|
mel.recompile_material(material)
|
||||||
|
|
||||||
|
# Save the asset
|
||||||
|
unreal.EditorAssetLibrary.save_asset(f"{material_path}/{material_name}")
|
||||||
|
|
||||||
|
unreal.log(f"Created material: {material_path}/{material_name}")
|
||||||
|
return material
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
create_phone_screen_material()
|
||||||
Reference in New Issue
Block a user