#include "pch.h" #include #include "GraphicsDevice/GraphicsUtility.h" #include "MetalDevice.h" #include "MetalGraphicsDevice.h" #include "MetalTexture2D.h" namespace unity { namespace webrtc { MetalGraphicsDevice::MetalGraphicsDevice( MetalDevice* device, UnityGfxRenderer renderer, ProfilerMarkerFactory* profiler) : IGraphicsDevice(renderer, profiler) , m_device(device) { } bool MetalGraphicsDevice::InitV() { m_queue = [m_device->Device() newCommandQueue]; return true; } void MetalGraphicsDevice::ShutdownV() { } ITexture2D* MetalGraphicsDevice::CreateDefaultTextureV(uint32_t w, uint32_t h, UnityRenderingExtTextureFormat textureFormat) { id device = m_device->Device(); MTLTextureDescriptor* textureDescriptor = [[MTLTextureDescriptor alloc] init]; textureDescriptor.pixelFormat = ConvertFormat(textureFormat); textureDescriptor.width = w; textureDescriptor.height = h; id texture = [device newTextureWithDescriptor:textureDescriptor]; return new MetalTexture2D(w, h, texture); } ITexture2D* MetalGraphicsDevice::CreateDefaultTextureFromNativeV(uint32_t w, uint32_t h, void* nativeTexturePtr) { id texture = (__bridge id)nativeTexturePtr; return new MetalTexture2D(w, h, texture); } bool MetalGraphicsDevice::CopyResourceV(ITexture2D* dest, ITexture2D* src) { id dstTexture = (__bridge id)dest->GetNativeTexturePtrV(); id srcTexture = (__bridge id)src->GetNativeTexturePtrV(); return CopyTexture(dstTexture, srcTexture); } bool MetalGraphicsDevice::CopyResourceFromNativeV(ITexture2D* dest, void* nativeTexturePtr) { if (nativeTexturePtr == nullptr) { return false; } id dstTexture = (__bridge id)dest->GetNativeTexturePtrV(); id srcTexture = (__bridge id)nativeTexturePtr; return CopyTexture(dstTexture, srcTexture); } bool MetalGraphicsDevice::CopyTexture(id dest, id src) { RTC_DCHECK_NE(dest, src); RTC_DCHECK_EQ(src.pixelFormat, dest.pixelFormat); RTC_DCHECK_EQ(src.width, dest.width); RTC_DCHECK_EQ(src.height, dest.height); m_device->EndCurrentCommandEncoder(); id commandBuffer = [m_queue commandBuffer]; id blit = [commandBuffer blitCommandEncoder]; NSUInteger width = src.width; NSUInteger height = src.height; MTLSize inTxtSize = MTLSizeMake(width, height, 1); MTLOrigin inTxtOrigin = MTLOriginMake(0, 0, 0); MTLOrigin outTxtOrigin = MTLOriginMake(0, 0, 0); [blit copyFromTexture:src sourceSlice:0 sourceLevel:0 sourceOrigin:inTxtOrigin sourceSize:inTxtSize toTexture:dest destinationSlice:0 destinationLevel:0 destinationOrigin:outTxtOrigin]; #if TARGET_OS_OSX // must be explicitly synchronized if the storageMode is Managed. if (dest.storageMode == MTLStorageModeManaged) [blit synchronizeResource:dest]; #endif [blit endEncoding]; // Commit the current command buffer and wait until the GPU process is completed. [commandBuffer commit]; [commandBuffer waitUntilCompleted]; return true; } //--------------------------------------------------------------------------------------------------------------------- ITexture2D* MetalGraphicsDevice::CreateCPUReadTextureV( uint32_t width, uint32_t height, UnityRenderingExtTextureFormat textureFormat) { id device = m_device->Device(); MTLTextureDescriptor* textureDescriptor = [[MTLTextureDescriptor alloc] init]; textureDescriptor.pixelFormat = ConvertFormat(textureFormat); textureDescriptor.width = width; textureDescriptor.height = height; if (@available(macOS 10.14, iOS 12.0, *)) { // This texture is a managed or shared resource. textureDescriptor.allowGPUOptimizedContents = false; } else { // Fallback on earlier versions } #if TARGET_OS_OSX textureDescriptor.storageMode = MTLStorageMode(MTLStorageModeManaged); #else textureDescriptor.storageMode = MTLStorageMode(MTLStorageModeShared); #endif id texture = [device newTextureWithDescriptor:textureDescriptor]; return new MetalTexture2D(width, height, texture); } rtc::scoped_refptr MetalGraphicsDevice::ConvertRGBToI420(ITexture2D* tex) { id source = (__bridge id)tex->GetNativeTexturePtrV(); const uint32_t width = tex->GetWidth(); const uint32_t height = tex->GetHeight(); RTC_DCHECK(source); RTC_DCHECK_GT(width, 0); RTC_DCHECK_GT(height, 0); const uint32_t BYTES_PER_PIXEL = 4; const uint32_t bytesPerRow = width * BYTES_PER_PIXEL; const uint32_t bufferSize = bytesPerRow * height; std::vector buffer; buffer.resize(bufferSize); [source getBytes:buffer.data() bytesPerRow:bytesPerRow fromRegion:MTLRegionMake2D(0, 0, width, height) mipmapLevel:0]; rtc::scoped_refptr i420_buffer = webrtc::I420Buffer::Create( static_cast(width), static_cast(height)); libyuv::ARGBToI420( buffer.data(), static_cast(bytesPerRow), i420_buffer->MutableDataY(), i420_buffer->StrideY(), i420_buffer->MutableDataU(), i420_buffer->StrideU(), i420_buffer->MutableDataV(), i420_buffer->StrideV(), static_cast(width), static_cast(height)); return i420_buffer; } MTLPixelFormat MetalGraphicsDevice::ConvertFormat(UnityRenderingExtTextureFormat format) { switch (format) { case kUnityRenderingExtFormatB8G8R8A8_SRGB: return MTLPixelFormatBGRA8Unorm_sRGB; case kUnityRenderingExtFormatB8G8R8A8_UNorm: return MTLPixelFormatBGRA8Unorm; case kUnityRenderingExtFormatR8G8B8A8_SRGB: return MTLPixelFormatRGBA8Unorm_sRGB; default: return MTLPixelFormatInvalid; } } } // end namespace webrtc } // end namespace unity