#include "SpoutPluginPrivatePCH.h" #include "../Public/SpoutBPFunctionLibrary.h" #include #include #include #include static ID3D11Device* g_D3D11Device; ID3D11DeviceContext* g_pImmediateContext = NULL; spoutSenderNames * sender; spoutGLDXinterop * interop; spoutDirectX * sdx; TArray FSenders; UMaterialInterface* BaseMaterial; FName TextureParameterName = "SpoutTexture"; void DestroyTexture(UTexture2D*& Texture) { // Here we destory the texture and its resource if (Texture){ Texture->RemoveFromRoot(); if (Texture->Resource) { BeginReleaseResource(Texture->Resource); FlushRenderingCommands(); } Texture->MarkPendingKill(); Texture = nullptr; }else{ UE_LOG(SpoutLog, Warning, TEXT("Texture is ready")); } } void ResetMatInstance(UTexture2D*& Texture, UMaterialInstanceDynamic*& MaterialInstance) { if (!Texture || !BaseMaterial || TextureParameterName.IsNone()) { UE_LOG(SpoutLog, Warning, TEXT("UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU")); return; } // Create material instance if (!MaterialInstance) { UMaterialInterface* mBase_Material = 0; mBase_Material = LoadObject(NULL, TEXT("/SpoutPlugin/Materials/SpoutMaterial.SpoutMaterial"), NULL, LOAD_None, NULL); BaseMaterial = mBase_Material; MaterialInstance = UMaterialInstanceDynamic::Create(BaseMaterial, NULL); if (!MaterialInstance) { UE_LOG(SpoutLog, Warning, TEXT("Material instance can't be created")); return; } } // Check again, we must have material instance if (!MaterialInstance) { UE_LOG(SpoutLog, Error, TEXT("Material instance wasn't created")); return; } // Check we have desired parameter UTexture* Tex = nullptr; if (!MaterialInstance->GetTextureParameterValue(TextureParameterName, Tex)) { UE_LOG(SpoutLog, Warning, TEXT("UI Material instance Texture parameter not found")); return; } MaterialInstance->SetTextureParameterValue(TextureParameterName, Texture); } void ResetTexture(UTexture2D*& Texture, UMaterialInstanceDynamic*& MaterialInstance, FSenderStruct*& SenderStruct) { // Here we init the texture to its initial state DestroyTexture(Texture); //UE_LOG(SpoutLog, Warning, TEXT("Texture is ready???????2222")); // init the new Texture2D Texture = UTexture2D::CreateTransient(SenderStruct->w, SenderStruct->h, PF_B8G8R8A8); Texture->AddToRoot(); Texture->UpdateResource(); //UE_LOG(SpoutLog, Warning, TEXT("Texture is ready???????333333")); SenderStruct->Texture2DResource = (FTexture2DResource*)Texture->Resource; //////////////////////////////////////////////////////////////////////////////// ResetMatInstance(Texture, MaterialInstance); } UTextureRenderTarget2D* USpoutBPFunctionLibrary::CreateTextureRenderTarget2D(int32 w, int32 h, EPixelFormat pixelFormat, bool forceLinearGamma) { UTextureRenderTarget2D* textureTarget = NewObject(); textureTarget->bNeedsTwoCopies = true; textureTarget->InitCustomFormat(w, h, pixelFormat, forceLinearGamma); textureTarget->AddressX = TextureAddress::TA_Wrap; textureTarget->AddressY = TextureAddress::TA_Wrap; #if WITH_EDITORONLY_DATA textureTarget->MipGenSettings = TextureMipGenSettings::TMGS_NoMipmaps; #endif textureTarget->AddToRoot(); //textureTarget->UpdateResourceW(); textureTarget->UpdateResource(); return textureTarget; } void initSpout() { UE_LOG(SpoutLog, Warning, TEXT("-----------> Init Spout")); sender = new spoutSenderNames; interop = new spoutGLDXinterop; sdx = new spoutDirectX; } void GetDevice() { UE_LOG(SpoutLog, Warning, TEXT("-----------> Set Graphics Device D3D11")); g_D3D11Device = (ID3D11Device*)GDynamicRHI->RHIGetNativeDevice(); g_D3D11Device->GetImmediateContext(&g_pImmediateContext); } int32 USpoutBPFunctionLibrary::SetMaxSenders(int32 max){ if (sender == nullptr) { initSpout(); }; sender->SetMaxSenders(max); return max; } void USpoutBPFunctionLibrary::GetMaxSenders(int32& max) { if (sender == nullptr) { initSpout(); }; max = sender->GetMaxSenders(); } bool GetSpoutRegistred(FName spoutName, FSenderStruct*& SenderStruct) { auto MyPredicate = [&](const FSenderStruct InItem) {return InItem.sName == spoutName; }; SenderStruct = FSenders.FindByPredicate(MyPredicate); bool bIsInListSenders = FSenders.ContainsByPredicate(MyPredicate); return bIsInListSenders; } void UnregisterSpout(FName spoutName) { auto MyPredicate = [&](const FSenderStruct InItem) {return InItem.sName == spoutName; }; FSenders.RemoveAll(MyPredicate); } void ClearRegister() { FSenders.Empty(); } FSenderStruct* RegisterReceiver(FName spoutName){ unsigned int w; unsigned int h; HANDLE sHandle; unsigned long format; sender->GetSenderInfo(spoutName.GetPlainANSIString(), w, h, sHandle, format); FSenderStruct* newFSenderStruc = new FSenderStruct(); newFSenderStruc->SetH(h); newFSenderStruc->SetW(w); newFSenderStruc->SetHandle(sHandle); newFSenderStruc->SetName(spoutName); newFSenderStruc->bIsAlive = true; newFSenderStruc->spoutType = ESpoutType::Receiver; newFSenderStruc->MaterialInstanceColor = nullptr; newFSenderStruc->TextureColor = nullptr; newFSenderStruc->sharedResource = nullptr; newFSenderStruc->rView = nullptr; newFSenderStruc->texTemp = NULL; //TextureColor = new UE_LOG(SpoutLog, Warning, TEXT("No material intance, creating...//////")); // Prepara Textura, Set the texture update region newFSenderStruc->UpdateRegions = new FUpdateTextureRegion2D(0, 0, 0, 0, newFSenderStruc->w, newFSenderStruc->h); ResetTexture(newFSenderStruc->TextureColor, newFSenderStruc->MaterialInstanceColor, newFSenderStruc); UE_LOG(SpoutLog, Warning, TEXT("--starting...--___Open Shared Resource___---")); HRESULT openResult = g_D3D11Device->OpenSharedResource(newFSenderStruc->sHandle, __uuidof(ID3D11Resource), (void**)(&newFSenderStruc->sharedResource)); if (FAILED(openResult)) { UE_LOG(SpoutLog, Error, TEXT("--FAIL--___Open Shared Resource___---")); return false; } UE_LOG(SpoutLog, Warning, TEXT("--starting...--___Create Shader Resource View___---")); HRESULT createShaderResourceViewResult = g_D3D11Device->CreateShaderResourceView(newFSenderStruc->sharedResource, NULL, &newFSenderStruc->rView); if (FAILED(createShaderResourceViewResult)) { UE_LOG(SpoutLog, Error, TEXT("--FAIL--___Create Shader Resource View___---")); return false; } //texture shared for the spout resource handle ID3D11Texture2D* tex = (ID3D11Texture2D*)newFSenderStruc->sharedResource; if (tex == nullptr) { UE_LOG(SpoutLog, Error, TEXT("---|||------||||----")); return false; } D3D11_TEXTURE2D_DESC description; tex->GetDesc(&description); description.Format = DXGI_FORMAT_B8G8R8A8_UNORM; description.Usage = D3D11_USAGE_STAGING; description.BindFlags = 0; description.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; description.MiscFlags = 0; description.MipLevels = 1; description.ArraySize = 1; UE_LOG(SpoutLog, Warning, TEXT("--- Creando d3d11 Texture 2D---")); HRESULT hr = g_D3D11Device->CreateTexture2D(&description, NULL, &newFSenderStruc->texTemp); if (FAILED(hr)) { std::stringstream ss; ss << " Error code = 0x" << std::hex << hr << std::endl; std::cout << ss.str() << std::endl; std::string TestString = ss.str(); UE_LOG(SpoutLog, Error, TEXT("Failed Create Texture: ----> %s"), *FString(TestString.c_str())); if (hr == E_OUTOFMEMORY) { UE_LOG(SpoutLog, Error, TEXT("OUT OF MEMORY")); } if (newFSenderStruc->texTemp) { newFSenderStruc->texTemp->Release(); newFSenderStruc->texTemp = NULL; } UE_LOG(SpoutLog, Error, TEXT("error creating temporal textura")); return false; } FSenders.Add(*newFSenderStruc); return newFSenderStruc; } bool USpoutBPFunctionLibrary::SpoutInfo(TArray& Senders){ Senders = FSenders; return true; } bool USpoutBPFunctionLibrary::SpoutInfoFrom(FName spoutName, FSenderStruct& SenderStruct){ //Existe en mi lista ?? auto MyPredicate = [&](const FSenderStruct InItem) {return InItem.sName == spoutName; }; FSenderStruct* EncontradoSenderStruct = FSenders.FindByPredicate(MyPredicate); if (EncontradoSenderStruct == nullptr){ UE_LOG(SpoutLog, Warning, TEXT("No Encontrado sender con nombre : %s"), *spoutName.GetPlainNameString()); return false; } else { SenderStruct = *EncontradoSenderStruct; } return true; } bool USpoutBPFunctionLibrary::CreateRegisterSender(FName spoutName, ID3D11Texture2D* baseTexture) { if (g_D3D11Device == nullptr || g_pImmediateContext == NULL){ UE_LOG(SpoutLog, Warning, TEXT("Getting Device...")); GetDevice(); } HANDLE sharedSendingHandle = NULL; bool texResult = false; bool updateResult = false; bool senderResult = false; D3D11_TEXTURE2D_DESC desc; baseTexture->GetDesc(&desc); ID3D11Texture2D * sendingTexture; UE_LOG(SpoutLog, Warning, TEXT("ID3D11Texture2D Info : ancho_%i, alto_%i"), desc.Width, desc.Height); UE_LOG(SpoutLog, Warning, TEXT("ID3D11Texture2D Info : Format is %i"), int(desc.Format)); //use the pixel format from basetexture (the native texture textureRenderTarget2D) DXGI_FORMAT texFormat = desc.Format; if (desc.Format == DXGI_FORMAT_B8G8R8A8_TYPELESS) { texFormat = DXGI_FORMAT_B8G8R8A8_UNORM; } texResult = sdx->CreateSharedDX11Texture(g_D3D11Device, desc.Width, desc.Height, texFormat, &sendingTexture, sharedSendingHandle); UE_LOG(SpoutLog, Warning, TEXT("Create shared Texture with SDX : %i"), texResult); if (!texResult) { UE_LOG(SpoutLog, Error, TEXT("SharedDX11Texture creation failed")); return 0; } const auto tmp = spoutName.GetPlainNameString(); UE_LOG(SpoutLog, Warning, TEXT("Created Sender: name --> %s"), *tmp); // senderResult = sender->CreateSender(spoutName.GetPlainANSIString(), desc.Width, desc.Height, sharedSendingHandle, texFormat); UE_LOG(SpoutLog, Warning, TEXT("Created sender DX11 with sender name : %s"), *tmp); // remove old sender register auto MyPredicate = [&](const FSenderStruct InItem) {return InItem.sName == spoutName; }; FSenders.RemoveAll(MyPredicate); // add new register UE_LOG(SpoutLog, Warning, TEXT("Adding Sender to Sender list")); FSenderStruct* newFSenderStruc = new FSenderStruct(); newFSenderStruc->SetW(desc.Width); newFSenderStruc->SetH(desc.Height); newFSenderStruc->SetName(spoutName); newFSenderStruc->bIsAlive = true; newFSenderStruc->spoutType = ESpoutType::Sender; newFSenderStruc->SetHandle(sharedSendingHandle); newFSenderStruc->activeTextures = sendingTexture; newFSenderStruc->MaterialInstanceColor = nullptr; newFSenderStruc->TextureColor = nullptr; FSenders.Add(*newFSenderStruc); return senderResult; } ESpoutState CheckSenderState(FName spoutName){ auto MyPredicate = [&](const FSenderStruct InItem) {return InItem.sName == spoutName; }; bool bIsInListSenders = FSenders.ContainsByPredicate(MyPredicate); ESpoutState state = ESpoutState::noEnoR; if (sender->FindSenderName(spoutName.GetPlainANSIString())) { //UE_LOG(SpoutLog, Warning, TEXT("Sender State: --> Exist")); if (bIsInListSenders) { //UE_LOG(SpoutLog, Warning, TEXT("Sender State: --> Exist y Registred")); state = ESpoutState::ER; } else { //UE_LOG(SpoutLog, Warning, TEXT("Sender State: --> Exist y No Registred")); state = ESpoutState::EnoR; } } else { //UE_LOG(SpoutLog, Warning, TEXT("Sender State: --> No Exist")); if (bIsInListSenders) { //UE_LOG(SpoutLog, Warning, TEXT("Sender State: --> No Exist y Registred")); state = ESpoutState::noER; } else { //UE_LOG(SpoutLog, Warning, TEXT("Sender State: --> No Exist y No Registred")); state = ESpoutState::noEnoR; } } return state; } bool USpoutBPFunctionLibrary::SpoutSender(FName spoutName, ESpoutSendTextureFrom sendTextureFrom, UTextureRenderTarget2D* textureRenderTarget2D, float targetGamma) { if (sender == nullptr) { initSpout(); }; if (g_D3D11Device == nullptr || g_pImmediateContext == NULL) { GetDevice(); } ID3D11Texture2D* baseTexture = 0; FSenderStruct* SenderStruct = 0; switch (sendTextureFrom) { case ESpoutSendTextureFrom::GameViewport: baseTexture = (ID3D11Texture2D*)GEngine->GameViewport->Viewport->GetRenderTargetTexture()->GetNativeResource(); break; case ESpoutSendTextureFrom::TextureRenderTarget2D: if (textureRenderTarget2D == nullptr) { UE_LOG(SpoutLog, Warning, TEXT("No TextureRenderTarget2D Selected!!")); return false; } textureRenderTarget2D->TargetGamma = targetGamma; baseTexture = (ID3D11Texture2D*)textureRenderTarget2D->Resource->TextureRHI->GetTexture2D()->GetNativeResource(); break; default: break; } if (baseTexture == nullptr) { UE_LOG(SpoutLog, Warning, TEXT("baseTexture is null")); return false; } ESpoutState state = CheckSenderState(spoutName); if (state == ESpoutState::noEnoR || state == ESpoutState::noER) { UE_LOG(SpoutLog, Warning, TEXT("New Sender creating, registering...")); CreateRegisterSender(spoutName, baseTexture); return false; } if (state == ESpoutState::EnoR) { UE_LOG(SpoutLog, Warning, TEXT("Already exist a Sender with the name %s"), *spoutName.GetPlainNameString()); return false; } if (state == ESpoutState::ER) { GetSpoutRegistred(spoutName, SenderStruct); if (SenderStruct->spoutType == ESpoutType::Sender) { // Check whether texture size has changed D3D11_TEXTURE2D_DESC td; baseTexture->GetDesc(&td); if (td.Width != SenderStruct->w || td.Height != SenderStruct->h) { UE_LOG(SpoutLog, Warning, TEXT("Texture Size has changed, Updating registered spout: "), *spoutName.GetPlainNameString()); UpdateRegisteredSpout(spoutName, baseTexture); return false; } } if (SenderStruct->spoutType == ESpoutType::Receiver) { UE_LOG(SpoutLog, Warning, TEXT("Already exist a Sender with the name %s and you have a receiver in unreal receiving"), *spoutName.GetPlainNameString()); return false; } } bool result = false; if (SenderStruct->activeTextures == nullptr) { UE_LOG(SpoutLog, Warning, TEXT("activeTextures is null")); return false; } HANDLE targetHandle = SenderStruct->sHandle; ID3D11Texture2D * targetTex = SenderStruct->activeTextures; if (targetTex == nullptr){ UE_LOG(SpoutLog, Warning, TEXT("targetTex is null")); return false; } //Sincroniza el thread del render y la copia de la textura ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER( void, ID3D11Texture2D*, tt, targetTex, ID3D11Texture2D*, bt, baseTexture, { g_pImmediateContext->CopyResource(tt, bt); g_pImmediateContext->Flush(); }); D3D11_TEXTURE2D_DESC td; baseTexture->GetDesc(&td); result = sender->UpdateSender(spoutName.GetPlainANSIString(), td.Width, td.Height, targetHandle); return result; } bool USpoutBPFunctionLibrary::SpoutReceiver(const FName spoutName, UMaterialInstanceDynamic*& mat, UTexture2D*& texture) { const FString SenderNameString = spoutName.GetPlainNameString(); if (BaseMaterial == NULL) { UMaterialInterface* mBase_Material = 0; mBase_Material = LoadObject(NULL, TEXT("/SpoutPlugin/Materials/SpoutMaterial.SpoutMaterial"), NULL, LOAD_None, NULL); BaseMaterial = mBase_Material; } if (sender == nullptr) { initSpout(); }; if (g_D3D11Device == nullptr || g_pImmediateContext == NULL){ GetDevice(); } ESpoutState state = CheckSenderState(spoutName); if (state == ESpoutState::noEnoR) { UE_LOG(SpoutLog, Warning, TEXT("Not found any sender and no registred with the name %s"), *spoutName.GetPlainNameString()); UE_LOG(SpoutLog, Warning, TEXT("try to rename it, or resend %s"), *SenderNameString); return false; } if(state == ESpoutState::noER) { UE_LOG(SpoutLog, Warning, TEXT("why are you registred??, unregistre, best close it")); //UnregisterSpout(spoutName); CloseSender(spoutName); return false; } if (state == ESpoutState::EnoR) { UE_LOG(SpoutLog, Warning, TEXT("Sender %s found, registring, receiving..."), *spoutName.GetPlainNameString()); RegisterReceiver(spoutName); return false; } if (state == ESpoutState::ER) { FSenderStruct* SenderStruct = 0; GetSpoutRegistred(spoutName, SenderStruct); if (SenderStruct->spoutType == ESpoutType::Sender) { //UE_LOG(SpoutLog, Warning, TEXT("Receiving from sender inside ue4 with the name %s"), *spoutName.GetPlainNameString()); } if (SenderStruct->spoutType == ESpoutType::Receiver) { //UE_LOG(SpoutLog, Warning, TEXT("Continue Receiver with the name %s"), *SenderName.GetPlainNameString()); //communication between the two threads (rendering thread and game thread) // copy pixels from shared resource texture to texture temporal and update ENQUEUE_UNIQUE_RENDER_COMMAND_FOURPARAMETER( void, ID3D11Texture2D*, t_texTemp, SenderStruct->texTemp, ID3D11Texture2D*, t_tex, (ID3D11Texture2D*)SenderStruct->sharedResource, int32, Stride, SenderStruct->w * 4, FSenderStruct*, Params, SenderStruct, { if (Params == nullptr) { return; } g_pImmediateContext->CopyResource(t_texTemp, t_tex); //g_pImmediateContext->Flush(); //<------ No Flush D3D11_MAPPED_SUBRESOURCE mapped; //Gets a pointer to the data contained in a subresource, and denies the GPU access to that subresource. // with CPU read permissions (D3D11_MAP_READ) HRESULT hr = g_pImmediateContext->Map(t_texTemp, 0, D3D11_MAP_READ, 0, &mapped); if (FAILED(hr)) { t_texTemp->Release(); return; } BYTE *pixel = (BYTE *)mapped.pData; g_pImmediateContext->Unmap(t_texTemp, 0); //Update Texture RHIUpdateTexture2D(Params->Texture2DResource->GetTexture2DRHI(), 0, *Params->UpdateRegions, mapped.RowPitch, (uint8*)pixel); }); texture = SenderStruct->TextureColor; mat = SenderStruct->MaterialInstanceColor; } } return true; } void USpoutBPFunctionLibrary::CloseSender(FName spoutName) { UE_LOG(SpoutLog, Warning, TEXT("Closing... sender %s "), *spoutName.GetPlainNameString()); if (sender == nullptr) { initSpout(); }; if (g_D3D11Device == nullptr || g_pImmediateContext == NULL) { GetDevice(); } ESpoutState state = CheckSenderState(spoutName); if (state == ESpoutState::noEnoR) { UE_LOG(SpoutLog, Warning, TEXT("already %s closed, there is nothing to close!! check the name"), *spoutName.GetPlainNameString()); //return; } if (state == ESpoutState::noER) { UE_LOG(SpoutLog, Warning, TEXT("+++++++++++++++")); UnregisterSpout(spoutName); //return; } if (state == ESpoutState::EnoR) { UE_LOG(SpoutLog, Warning, TEXT("Already exist a Sender with the name %s, You can not close a sender that is not yours.??"), *spoutName.GetPlainNameString()); //return; } if (state == ESpoutState::ER) { FSenderStruct* tempSenderStruct = 0; GetSpoutRegistred(spoutName, tempSenderStruct); if (tempSenderStruct->spoutType == ESpoutType::Sender) { UE_LOG(SpoutLog, Warning, TEXT("releasing sender %s"), *spoutName.GetPlainNameString()); // here really release the sender sender->ReleaseSenderName(spoutName.GetPlainANSIString()); UE_LOG(SpoutLog, Warning, TEXT("sender %s released"), *spoutName.GetPlainNameString()); } else { UE_LOG(SpoutLog, Warning, TEXT("receiver always listening")); FlushRenderingCommands(); if (tempSenderStruct->sharedResource != nullptr) { UE_LOG(SpoutLog, Warning, TEXT("Release sharedResource")); tempSenderStruct->sharedResource->Release(); } if (tempSenderStruct->texTemp != nullptr) { UE_LOG(SpoutLog, Warning, TEXT("Release Temporal texTemp")); tempSenderStruct->texTemp->Release(); } if (tempSenderStruct->rView != nullptr) { UE_LOG(SpoutLog, Warning, TEXT("Release rView")); tempSenderStruct->rView->Release(); } UE_LOG(SpoutLog, Warning, TEXT("Released All Temporal Textures")); //return; } UnregisterSpout(spoutName); } UE_LOG(SpoutLog, Warning, TEXT("There are now %i senders remaining "), FSenders.Num()); } bool USpoutBPFunctionLibrary::UpdateRegisteredSpout(FName spoutName, ID3D11Texture2D * baseTexture) { HANDLE sharedSendingHandle = NULL; bool texResult = false; bool updateResult = false; bool senderResult = false; D3D11_TEXTURE2D_DESC desc; baseTexture->GetDesc(&desc); ID3D11Texture2D * sendingTexture; UE_LOG(SpoutLog, Warning, TEXT("ID3D11Texture2D Info : ancho_%i, alto_%i"), desc.Width, desc.Height); UE_LOG(SpoutLog, Warning, TEXT("ID3D11Texture2D Info : Format is %i"), int(desc.Format)); //use the pixel format from basetexture (the native texture textureRenderTarget2D) DXGI_FORMAT texFormat = desc.Format; if (desc.Format == DXGI_FORMAT_B8G8R8A8_TYPELESS) { texFormat = DXGI_FORMAT_B8G8R8A8_UNORM; } texResult = sdx->CreateSharedDX11Texture(g_D3D11Device, desc.Width, desc.Height, texFormat, &sendingTexture, sharedSendingHandle); UE_LOG(SpoutLog, Warning, TEXT("Create shared Texture with SDX : %i"), texResult); if (!texResult) { UE_LOG(SpoutLog, Error, TEXT("SharedDX11Texture creation failed")); return 0; } // update register UE_LOG(SpoutLog, Warning, TEXT("Updating Sender in Sender list")); for (int32 Index = 0; Index != FSenders.Num(); ++Index) { if (FSenders[Index].sName == spoutName) { FSenders[Index].SetW(desc.Width); FSenders[Index].SetH(desc.Height); FSenders[Index].SetName(spoutName); FSenders[Index].bIsAlive = true; FSenders[Index].spoutType = ESpoutType::Sender; FSenders[Index].SetHandle(sharedSendingHandle); FSenders[Index].activeTextures = sendingTexture; FSenders[Index].MaterialInstanceColor = nullptr; FSenders[Index].TextureColor = nullptr; senderResult = true; } } return senderResult; }