// Copyright Epic Games, Inc. All Rights Reserved. #include "OpenGL/SlateOpenGLRenderer.h" #include "Fonts/FontTypes.h" #include "Fonts/FontCache.h" #include "Types/SlateVector2.h" #include "Widgets/SWindow.h" #include "OpenGL/SlateOpenGLTextures.h" #include "OpenGL/SlateOpenGLTextureManager.h" #include "OpenGL/SlateOpenGLRenderingPolicy.h" #include "Rendering/ElementBatcher.h" #if PLATFORM_LINUX #include "HAL/PlatformApplicationMisc.h" #elif PLATFORM_MAC #include "Mac/CocoaThread.h" #endif class FSlateOpenGLFontAtlasFactory : public ISlateFontAtlasFactory { public: virtual ~FSlateOpenGLFontAtlasFactory() { } virtual FIntPoint GetAtlasSize(ESlateFontAtlasContentType InContentType) const override { switch (InContentType) { case ESlateFontAtlasContentType::Alpha: return FIntPoint(GrayscaleTextureSize, GrayscaleTextureSize); case ESlateFontAtlasContentType::Color: return FIntPoint(ColorTextureSize, ColorTextureSize); case ESlateFontAtlasContentType::Msdf: return FIntPoint(SdfTextureSize, SdfTextureSize); default: checkNoEntry(); // Default to COLOR return FIntPoint(ColorTextureSize, ColorTextureSize); } } virtual TSharedRef CreateFontAtlas(ESlateFontAtlasContentType InContentType) const override { const FIntPoint AtlasSize = GetAtlasSize(InContentType); TSharedRef FontTexture = MakeShareable(new FSlateFontTextureOpenGL(AtlasSize.X, AtlasSize.Y, InContentType)); FontTexture->CreateFontTexture(); return FontTexture; } virtual TSharedPtr CreateNonAtlasedTexture(const uint32 InWidth, const uint32 InHeight, ESlateFontAtlasContentType InContentType, const TArray& InRawData) const override { return nullptr; } private: /** Size of each font texture, width and height */ static const uint32 GrayscaleTextureSize = 1024; static const uint32 ColorTextureSize = 512; static const uint32 SdfTextureSize = 1024; }; TSharedRef CreateOpenGLFontServices() { const TSharedRef FontCache = MakeShareable(new FSlateFontCache(MakeShareable(new FSlateOpenGLFontAtlasFactory), ESlateTextureAtlasThreadId::Game)); return MakeShareable(new FSlateFontServices(FontCache, FontCache)); } FSlateOpenGLRenderer::FSlateOpenGLRenderer( const ISlateStyle& InStyle ) : FSlateRenderer( CreateOpenGLFontServices() ) , Style( InStyle ) { ViewMatrix = FMatrix( FPlane(1, 0, 0, 0), FPlane(0, 1, 0, 0), FPlane(0, 0, 1, 0), FPlane(0, 0, 0, 1)); #if PLATFORM_LINUX FPlatformApplicationMisc::UsingOpenGL(); #endif // PLATFORM_LINUX } FSlateOpenGLRenderer::~FSlateOpenGLRenderer() { } /** Returns a draw buffer that can be used by Slate windows to draw window elements */ FSlateDrawBuffer& FSlateOpenGLRenderer::AcquireDrawBuffer() { ensureMsgf(!DrawBuffer.IsLocked(), TEXT("The DrawBuffer is already locked. Make sure to ReleaseDrawBuffer the DrawBuffer")); DrawBuffer.Lock(); // Clear out the buffer each time its accessed DrawBuffer.ClearBuffer(); return DrawBuffer; } void FSlateOpenGLRenderer::ReleaseDrawBuffer(FSlateDrawBuffer& InWindowDrawBuffer) { ensureMsgf(&DrawBuffer == &InWindowDrawBuffer, TEXT("It release a DrawBuffer that is not a member of the SlateNullRenderer")); InWindowDrawBuffer.Unlock(); } bool FSlateOpenGLRenderer::Initialize() { SharedContext.Initialize( NULL, NULL ); TextureManager = MakeShareable( new FSlateOpenGLTextureManager ); RenderingPolicy = MakeShareable( new FSlateOpenGLRenderingPolicy( SlateFontServices.ToSharedRef(), TextureManager.ToSharedRef() ) ); ElementBatcher = MakeUnique(RenderingPolicy.ToSharedRef()); #if !PLATFORM_USES_GLES // Load OpenGL extensions if needed. Need a current rendering context to do this LoadOpenGLExtensions(); #endif TextureManager->LoadUsedTextures(); // Create rendering resources if needed RenderingPolicy->ConditionalInitializeResources(); return true; } /** * Creates necessary resources to render a window and sends draw commands to the rendering thread * * @param WindowDrawBuffer The buffer containing elements to draw */ void FSlateOpenGLRenderer::DrawWindows( FSlateDrawBuffer& InWindowDrawBuffer ) { const TSharedRef FontCache = SlateFontServices->GetFontCache(); // Draw each window. For performance. All elements are batched before anything is rendered const TArray< TSharedRef >& WindowElementLists = InWindowDrawBuffer.GetWindowElementLists(); for( int32 ListIndex = 0; ListIndex < WindowElementLists.Num(); ++ListIndex ) { FSlateWindowElementList& ElementList = *WindowElementLists[ListIndex]; if ( ElementList.GetRenderWindow() ) { SWindow* WindowToDraw = ElementList.GetRenderWindow(); const FVector2D WindowSize = WindowToDraw->GetSizeInScreen(); if (WindowSize.X > 0 && WindowSize.Y > 0) { FSlateOpenGLViewport* Viewport = WindowToViewportMap.Find( WindowToDraw ); check(Viewport); //@todo Slate OpenGL: Move this to ResizeViewport if( WindowSize.X != Viewport->ViewportRect.Right || WindowSize.Y != Viewport->ViewportRect.Bottom ) { //@todo implement fullscreen const bool bFullscreen = false; Private_ResizeViewport( UE::Slate::CastToVector2f(WindowSize), *Viewport, bFullscreen ); } Viewport->MakeCurrent(); // Update texture cache of pending requests before the resources are accessed during batching TextureManager->UpdateCache(); // Batch elements. Note that we must set the current viewport before doing this so we have a valid rendering context when calling OpenGL functions ElementBatcher->AddElements(ElementList); // Update the font cache with new text after elements are batched FontCache->UpdateCache(); //@ todo Slate: implement for opengl bool bRequiresStencilTest = false; ElementBatcher->ResetBatches(); FSlateBatchData& BatchData = ElementList.GetBatchData(); RenderingPolicy->BuildRenderingBuffers( BatchData ); check(Viewport); glViewport( Viewport->ViewportRect.Left, Viewport->ViewportRect.Top, Viewport->ViewportRect.Right, Viewport->ViewportRect.Bottom ); if (BatchData.GetRenderBatches().Num() > 0) { // Draw all elements RenderingPolicy->DrawElements( ViewMatrix*Viewport->ProjectionMatrix, WindowSize, BatchData.GetRenderBatches() ); } Viewport->SwapBuffers(); // All elements have been drawn. Reset all cached data ElementBatcher->ResetBatches(); } } } // flush the cache if needed FontCache->ConditionalFlushCache(); TextureManager->ConditionalFlushCache(); // Safely release the references now that we are finished rendering with the dynamic brushes DynamicBrushesToRemove.Empty(); } /** Called when a window is destroyed to give the renderer a chance to free resources */ void FSlateOpenGLRenderer::OnWindowDestroyed( const TSharedRef& InWindow ) { FSlateOpenGLViewport* Viewport = WindowToViewportMap.Find( &InWindow.Get() ); if( Viewport ) { Viewport->Destroy(); } WindowToViewportMap.Remove( &InWindow.Get() ); SharedContext.MakeCurrent(); } void FSlateOpenGLRenderer::CreateViewport( const TSharedRef InWindow ) { #if UE_BUILD_DEBUG // Ensure a viewport for this window doesnt already exist FSlateOpenGLViewport* Viewport = WindowToViewportMap.Find( &InWindow.Get() ); check(!Viewport); #endif FSlateOpenGLViewport& NewViewport = WindowToViewportMap.Add( &InWindow.Get(), FSlateOpenGLViewport() ); NewViewport.Initialize( InWindow, SharedContext ); } void FSlateOpenGLRenderer::RequestResize( const TSharedPtr& InWindow, uint32 NewSizeX, uint32 NewSizeY ) { // @todo implement. Viewports are currently resized in DrawWindows } void FSlateOpenGLRenderer::Private_ResizeViewport( FVector2f WindowSize, FSlateOpenGLViewport& InViewport, bool bFullscreen ) { uint32 Width = FMath::TruncToInt(WindowSize.X); uint32 Height = FMath::TruncToInt(WindowSize.Y); InViewport.Resize( Width, Height, bFullscreen ); } void FSlateOpenGLRenderer::UpdateFullscreenState( const TSharedRef InWindow, uint32 OverrideResX, uint32 OverrideResY ) { FSlateOpenGLViewport* Viewport = WindowToViewportMap.Find( &InWindow.Get() ); if( Viewport ) { bool bFullscreen = IsViewportFullscreen( *InWindow ); // todo: support Fullscreen modes in OpenGL // uint32 ResX = OverrideResX ? OverrideResX : GSystemResolution.ResX; // uint32 ResY = OverrideResY ? OverrideResY : GSystemResolution.ResY; Private_ResizeViewport( FVector2f( Viewport->ViewportRect.Right, Viewport->ViewportRect.Bottom ), *Viewport, bFullscreen ); } } void FSlateOpenGLRenderer::ReleaseDynamicResource( const FSlateBrush& Brush ) { TextureManager->ReleaseDynamicTextureResource( Brush ); } bool FSlateOpenGLRenderer::GenerateDynamicImageResource(FName ResourceName, uint32 Width, uint32 Height, const TArray< uint8 >& Bytes) { return TextureManager->CreateDynamicTextureResource(ResourceName, Width, Height, Bytes) != NULL; } bool FSlateOpenGLRenderer::GenerateDynamicImageResource(FName ResourceName, FSlateTextureDataRef TextureData) { return GenerateDynamicImageResource(ResourceName, TextureData->GetWidth(), TextureData->GetHeight(), TextureData->GetRawBytes()); } FSlateResourceHandle FSlateOpenGLRenderer::GetResourceHandle(const FSlateBrush& Brush, FVector2f LocalSize, float DrawScale) { return TextureManager->GetResourceHandle(Brush, LocalSize, DrawScale); } void FSlateOpenGLRenderer::RemoveDynamicBrushResource( TSharedPtr BrushToRemove ) { DynamicBrushesToRemove.Add( BrushToRemove ); } void FSlateOpenGLRenderer::LoadStyleResources( const ISlateStyle& InStyle ) { if ( TextureManager.IsValid() ) { TextureManager->LoadStyleResources( InStyle ); } } FSlateUpdatableTexture* FSlateOpenGLRenderer::CreateUpdatableTexture(uint32 Width, uint32 Height) { TArray RawData; RawData.AddZeroed(Width * Height * 4); FSlateOpenGLTexture* NewTexture = new FSlateOpenGLTexture(Width, Height); NewTexture->Init(GL_RGBA, RawData); return NewTexture; } FSlateUpdatableTexture* FSlateOpenGLRenderer::CreateSharedHandleTexture(void* SharedHandle) { FSlateOpenGLTexture* NewTexture = new FSlateOpenGLTexture(); NewTexture->Init(SharedHandle); return NewTexture; } void FSlateOpenGLRenderer::ReleaseUpdatableTexture(FSlateUpdatableTexture* Texture) { Texture->Cleanup(); } ISlateAtlasProvider* FSlateOpenGLRenderer::GetTextureAtlasProvider() { if( TextureManager.IsValid() ) { return TextureManager->GetTextureAtlasProvider(); } return nullptr; } FCriticalSection* FSlateOpenGLRenderer::GetResourceCriticalSection() { return &ResourceCriticalSection; } int32 FSlateOpenGLRenderer::RegisterCurrentScene(FSceneInterface* Scene) { // This is a no-op return -1; } int32 FSlateOpenGLRenderer::GetCurrentSceneIndex() const { // This is a no-op return -1; } void FSlateOpenGLRenderer::SetCurrentSceneIndex(int32 InIndex) { // This is a no-op } void FSlateOpenGLRenderer::ClearScenes() { // This is a no-op }