Files
UnrealEngineUWP/Engine/Source/Runtime/OpenGLDrv/Private/OpenGLTexture.cpp
Ben Marsh 4ba423868f Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340)
#lockdown Nick.Penwarden
#rb none

==========================
MAJOR FEATURES + CHANGES
==========================

Change 3209340 on 2016/11/23 by Ben.Marsh

	Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h.

	Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms.

	  * Every header now includes everything it needs to compile.
	        * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first.
	        * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h.
	  * Every .cpp file includes its matching .h file first.
	        * This helps validate that each header is including everything it needs to compile.
	  * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more.
	        * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there.
	        * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible.
	  * No engine code explicitly includes a precompiled header any more.
	        * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies.
	        * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files.

	Tool used to generate this transform is at Engine\Source\Programs\IncludeTool.

[CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00

2290 lines
79 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
OpenGLVertexBuffer.cpp: OpenGL texture RHI implementation.
=============================================================================*/
#include "CoreMinimal.h"
#include "Containers/ResourceArray.h"
#include "Stats/Stats.h"
#include "RHI.h"
#include "RenderUtils.h"
#include "ShaderCache.h"
#include "OpenGLDrv.h"
#include "OpenGLDrvPrivate.h"
/*-----------------------------------------------------------------------------
Texture allocator support.
-----------------------------------------------------------------------------*/
/** Caching it here, to avoid getting it every time we create a texture. 0 is no multisampling. */
GLint GMaxOpenGLColorSamples = 0;
GLint GMaxOpenGLDepthSamples = 0;
GLint GMaxOpenGLIntegerSamples = 0;
// in bytes, never change after RHI, needed to scale game features
int64 GOpenGLDedicatedVideoMemory = 0;
// In bytes. Never changed after RHI init. Our estimate of the amount of memory that we can use for graphics resources in total.
int64 GOpenGLTotalGraphicsMemory = 0;
static bool ShouldCountAsTextureMemory(uint32 Flags)
{
return (Flags & (TexCreate_RenderTargetable | TexCreate_ResolveTargetable | TexCreate_DepthStencilTargetable)) == 0;
}
void OpenGLTextureAllocated(FRHITexture* Texture, uint32 Flags)
{
int32 TextureSize = 0;
FOpenGLTextureCube* TextureCube = 0;
FOpenGLTexture2D* Texture2D = 0;
FOpenGLTexture2DArray* Texture2DArray = 0;
FOpenGLTexture3D* Texture3D = 0;
bool bRenderTarget = !ShouldCountAsTextureMemory(Flags);
if (( TextureCube = (FOpenGLTextureCube*)Texture->GetTextureCube()) != NULL)
{
TextureSize = CalcTextureSize( TextureCube->GetSize(), TextureCube->GetSize(), TextureCube->GetFormat(), TextureCube->GetNumMips() );
TextureSize *= TextureCube->GetArraySize() * (TextureCube->GetArraySize() == 1 ? 6 : 1);
TextureCube->SetMemorySize( TextureSize );
TextureCube->SetIsPowerOfTwo(FMath::IsPowerOfTwo(TextureCube->GetSizeX()) && FMath::IsPowerOfTwo(TextureCube->GetSizeY()));
if (bRenderTarget)
{
INC_MEMORY_STAT_BY(STAT_RenderTargetMemoryCube,TextureSize);
}
else
{
INC_MEMORY_STAT_BY(STAT_TextureMemoryCube,TextureSize);
}
}
else if ((Texture2D = (FOpenGLTexture2D*)Texture->GetTexture2D()) != NULL)
{
TextureSize = CalcTextureSize( Texture2D->GetSizeX(), Texture2D->GetSizeY(), Texture2D->GetFormat(), Texture2D->GetNumMips() )*Texture2D->GetNumSamples();
Texture2D->SetMemorySize( TextureSize );
Texture2D->SetIsPowerOfTwo(FMath::IsPowerOfTwo(Texture2D->GetSizeX()) && FMath::IsPowerOfTwo(Texture2D->GetSizeY()));
if (bRenderTarget)
{
INC_MEMORY_STAT_BY(STAT_RenderTargetMemory2D,TextureSize);
}
else
{
INC_MEMORY_STAT_BY(STAT_TextureMemory2D,TextureSize);
}
}
else if ((Texture3D = (FOpenGLTexture3D*)Texture->GetTexture3D()) != NULL)
{
TextureSize = CalcTextureSize3D( Texture3D->GetSizeX(), Texture3D->GetSizeY(), Texture3D->GetSizeZ(), Texture3D->GetFormat(), Texture3D->GetNumMips() );
Texture3D->SetMemorySize( TextureSize );
Texture3D->SetIsPowerOfTwo(FMath::IsPowerOfTwo(Texture3D->GetSizeX()) && FMath::IsPowerOfTwo(Texture3D->GetSizeY()) && FMath::IsPowerOfTwo(Texture3D->GetSizeZ()));
if (bRenderTarget)
{
INC_MEMORY_STAT_BY(STAT_RenderTargetMemory3D,TextureSize);
}
else
{
INC_MEMORY_STAT_BY(STAT_TextureMemory3D,TextureSize);
}
}
else if ((Texture2DArray = (FOpenGLTexture2DArray*)Texture->GetTexture2DArray()) != NULL)
{
TextureSize = Texture2DArray->GetSizeZ() * CalcTextureSize( Texture2DArray->GetSizeX(), Texture2DArray->GetSizeY(), Texture2DArray->GetFormat(), Texture2DArray->GetNumMips() );
Texture2DArray->SetMemorySize( TextureSize );
Texture2DArray->SetIsPowerOfTwo(FMath::IsPowerOfTwo(Texture2DArray->GetSizeX()) && FMath::IsPowerOfTwo(Texture2DArray->GetSizeY()));
if (bRenderTarget)
{
INC_MEMORY_STAT_BY(STAT_RenderTargetMemory2D,TextureSize);
}
else
{
INC_MEMORY_STAT_BY(STAT_TextureMemory2D,TextureSize);
}
}
else
{
check(0); // Add handling of other texture types
}
if( bRenderTarget )
{
GCurrentRendertargetMemorySize += Align(TextureSize, 1024) / 1024;
}
else
{
GCurrentTextureMemorySize += Align(TextureSize, 1024) / 1024;
}
}
void OpenGLTextureDeleted( FRHITexture* Texture )
{
FShaderCache::RemoveTexture(Texture);
bool bRenderTarget = !ShouldCountAsTextureMemory(Texture->GetFlags());
int32 TextureSize = 0;
if (Texture->GetTextureCube())
{
TextureSize = ((FOpenGLTextureCube*)Texture->GetTextureCube())->GetMemorySize();
if (bRenderTarget)
{
DEC_MEMORY_STAT_BY(STAT_RenderTargetMemory3D,TextureSize);
}
else
{
DEC_MEMORY_STAT_BY(STAT_TextureMemory3D,TextureSize);
}
}
else if (Texture->GetTexture2D())
{
TextureSize = ((FOpenGLTexture2D*)Texture->GetTexture2D())->GetMemorySize();
if (bRenderTarget)
{
DEC_MEMORY_STAT_BY(STAT_RenderTargetMemory2D,TextureSize);
}
else
{
DEC_MEMORY_STAT_BY(STAT_TextureMemory2D,TextureSize);
}
}
else if (Texture->GetTexture3D())
{
TextureSize = ((FOpenGLTexture3D*)Texture->GetTexture3D())->GetMemorySize();
if (bRenderTarget)
{
DEC_MEMORY_STAT_BY(STAT_RenderTargetMemory3D,TextureSize);
}
else
{
DEC_MEMORY_STAT_BY(STAT_TextureMemory3D,TextureSize);
}
}
else if (Texture->GetTexture2DArray())
{
TextureSize = ((FOpenGLTexture2DArray*)Texture->GetTexture2DArray())->GetMemorySize();
if (bRenderTarget)
{
DEC_MEMORY_STAT_BY(STAT_RenderTargetMemory2D,TextureSize);
}
else
{
DEC_MEMORY_STAT_BY(STAT_TextureMemory2D,TextureSize);
}
}
else
{
check(0); // Add handling of other texture types
}
if( bRenderTarget )
{
GCurrentRendertargetMemorySize -= Align(TextureSize, 1024) / 1024;
}
else
{
GCurrentTextureMemorySize -= Align(TextureSize, 1024) / 1024;
}
}
uint64 FOpenGLDynamicRHI::RHICalcTexture2DPlatformSize(uint32 SizeX, uint32 SizeY, uint8 Format, uint32 NumMips, uint32 NumSamples, uint32 Flags, uint32& OutAlign)
{
OutAlign = 0;
return CalcTextureSize(SizeX, SizeY, (EPixelFormat)Format, NumMips);
}
uint64 FOpenGLDynamicRHI::RHICalcTexture3DPlatformSize(uint32 SizeX, uint32 SizeY, uint32 SizeZ, uint8 Format, uint32 NumMips, uint32 Flags, uint32& OutAlign)
{
OutAlign = 0;
return CalcTextureSize3D(SizeX, SizeY, SizeZ, (EPixelFormat)Format, NumMips);
}
uint64 FOpenGLDynamicRHI::RHICalcTextureCubePlatformSize(uint32 Size, uint8 Format, uint32 NumMips, uint32 Flags, uint32& OutAlign)
{
OutAlign = 0;
return CalcTextureSize(Size, Size, (EPixelFormat)Format, NumMips) * 6;
}
/**
* Retrieves texture memory stats. Unsupported with this allocator.
*
* @return false, indicating that out variables were left unchanged.
*/
void FOpenGLDynamicRHI::RHIGetTextureMemoryStats(FTextureMemoryStats& OutStats)
{
OutStats.DedicatedVideoMemory = GOpenGLDedicatedVideoMemory;
OutStats.DedicatedSystemMemory = 0;
OutStats.SharedSystemMemory = 0;
OutStats.TotalGraphicsMemory = GOpenGLTotalGraphicsMemory ? GOpenGLTotalGraphicsMemory : -1;
OutStats.AllocatedMemorySize = int64(GCurrentTextureMemorySize) * 1024;
OutStats.LargestContiguousAllocation = OutStats.AllocatedMemorySize;
OutStats.TexturePoolSize = GTexturePoolSize;
OutStats.PendingMemoryAdjustment = 0;
}
/**
* Fills a texture with to visualize the texture pool memory.
*
* @param TextureData Start address
* @param SizeX Number of pixels along X
* @param SizeY Number of pixels along Y
* @param Pitch Number of bytes between each row
* @param PixelSize Number of bytes each pixel represents
*
* @return true if successful, false otherwise
*/
bool FOpenGLDynamicRHI::RHIGetTextureMemoryVisualizeData( FColor* /*TextureData*/, int32 /*SizeX*/, int32 /*SizeY*/, int32 /*Pitch*/, int32 /*PixelSize*/ )
{
return false;
}
FRHITexture* FOpenGLDynamicRHI::CreateOpenGLTexture(uint32 SizeX, uint32 SizeY, bool bCubeTexture, bool bArrayTexture, uint8 Format, uint32 NumMips, uint32 NumSamples, uint32 ArraySize, uint32 Flags, const FClearValueBinding& InClearValue, FResourceBulkDataInterface* BulkData)
{
VERIFY_GL_SCOPE();
SCOPE_CYCLE_COUNTER(STAT_OpenGLCreateTextureTime);
bool bAllocatedStorage = false;
if(NumMips == 0)
{
if(NumSamples <= 1)
{
NumMips = FindMaxMipmapLevel(SizeX, SizeY);
}
else
{
NumMips = 1;
}
}
#if UE_BUILD_DEBUG
check( !( NumSamples > 1 && bCubeTexture) );
check( bArrayTexture != (ArraySize == 1));
#endif
bool bNoSRGBSupport = (GMaxRHIFeatureLevel <= ERHIFeatureLevel::ES3_1);
if ((Flags & TexCreate_RenderTargetable) && Format == PF_B8G8R8A8 && !FOpenGL::SupportsBGRA8888RenderTarget())
{
// Some android devices does not support BGRA as a color attachment
Format = PF_R8G8B8A8;
}
if (bNoSRGBSupport)
{
// Remove sRGB read flag when not supported
Flags &= ~TexCreate_SRGB;
}
GLuint TextureID = 0;
FOpenGL::GenTextures(1, &TextureID);
GLenum Target = GL_NONE;
if(bCubeTexture)
{
if ( FOpenGL::SupportsTexture3D() )
{
Target = bArrayTexture ? GL_TEXTURE_CUBE_MAP_ARRAY : GL_TEXTURE_CUBE_MAP;
}
else
{
check(!bArrayTexture);
Target = GL_TEXTURE_CUBE_MAP;
}
check(SizeX == SizeY);
}
else
{
Target = (NumSamples > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D;
// @todo: refactor 2d texture array support here?
check(!bArrayTexture);
}
check(Target != GL_NONE);
const bool bSRGB = (Flags&TexCreate_SRGB) != 0;
const FOpenGLTextureFormat& GLFormat = GOpenGLTextureFormats[Format];
if (GLFormat.InternalFormat[bSRGB] == GL_NONE)
{
UE_LOG(LogRHI, Fatal,TEXT("Texture format '%s' not supported (sRGB=%d)."), GPixelFormats[Format].Name, bSRGB);
}
FOpenGLContextState& ContextState = GetContextStateForCurrentContext();
// Make sure PBO is disabled
CachedBindPixelUnpackBuffer(ContextState,0);
// Use a texture stage that's not likely to be used for draws, to avoid waiting
CachedSetupTextureStage(ContextState, FOpenGL::GetMaxCombinedTextureImageUnits() - 1, Target, TextureID, 0, NumMips);
// For client storage textures we allocate a single backing store buffer.
uint8* TextureRange = nullptr;
if (NumSamples == 1)
{
if (!FMath::IsPowerOfTwo(SizeX) || !FMath::IsPowerOfTwo(SizeY))
{
glTexParameteri(Target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(Target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if ( FOpenGL::SupportsTexture3D() )
{
glTexParameteri(Target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
}
}
else
{
glTexParameteri(Target, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(Target, GL_TEXTURE_WRAP_T, GL_REPEAT);
if ( FOpenGL::SupportsTexture3D() )
{
glTexParameteri(Target, GL_TEXTURE_WRAP_R, GL_REPEAT);
}
}
glTexParameteri(Target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(Target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
if( FOpenGL::SupportsTextureFilterAnisotropic() )
{
glTexParameteri(Target, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);
}
if ( FOpenGL::SupportsTextureBaseLevel() )
{
glTexParameteri(Target, GL_TEXTURE_BASE_LEVEL, 0);
}
if ( FOpenGL::SupportsTextureMaxLevel() )
{
glTexParameteri(Target, GL_TEXTURE_MAX_LEVEL, NumMips - 1);
}
TextureMipLimits.Add(TextureID, TPairInitializer<GLenum, GLenum>(0, NumMips - 1));
if (FOpenGL::SupportsTextureSwizzle() && GLFormat.bBGRA && !(Flags & TexCreate_RenderTargetable))
{
glTexParameteri(Target, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
glTexParameteri(Target, GL_TEXTURE_SWIZZLE_B, GL_RED);
}
if (bArrayTexture)
{
FOpenGL::TexStorage3D( Target, NumMips, GLFormat.InternalFormat[bSRGB], SizeX, SizeY, ArraySize, GLFormat.Format, GLFormat.Type);
}
else
{
// Should we use client-storage to improve update time on platforms that require it
bool const bRenderable = (Flags & (TexCreate_RenderTargetable|TexCreate_ResolveTargetable|TexCreate_DepthStencilTargetable|TexCreate_CPUReadback)) != 0;
bool const bUseClientStorage = (FOpenGL::SupportsClientStorage() && !FOpenGL::SupportsTextureView() && !bRenderable && !GLFormat.bCompressed);
if(bUseClientStorage)
{
const bool bIsCubeTexture = Target == GL_TEXTURE_CUBE_MAP;
const uint32 TextureSize = CalcTextureSize(SizeX, SizeY, (EPixelFormat)Format, NumMips) * (bIsCubeTexture ? 6 : 1);
const GLenum FirstTarget = bIsCubeTexture ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : Target;
const uint32 NumTargets = bIsCubeTexture ? 6 : 1;
TextureRange = new uint8[TextureSize];
check(TextureRange);
if(FOpenGL::SupportsTextureRange())
{
FOpenGL::TextureRange(Target, TextureSize, TextureRange);
glTexParameteri(Target, GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE);
}
glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
uint8* MipPointer = TextureRange;
for(uint32 MipIndex = 0; MipIndex < uint32(NumMips); MipIndex++)
{
const uint32 MipSize = CalcTextureMipMapSize(SizeX, SizeY, (EPixelFormat)Format, MipIndex);
for(uint32 TargetIndex = 0; TargetIndex < NumTargets; TargetIndex++)
{
glTexImage2D(
FirstTarget + TargetIndex,
MipIndex,
GLFormat.InternalFormat[bSRGB],
FMath::Max<uint32>(1,(SizeX >> MipIndex)),
FMath::Max<uint32>(1,(SizeY >> MipIndex)),
0,
GLFormat.Format,
GLFormat.Type,
MipPointer
);
MipPointer += MipSize;
}
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
if(FOpenGL::SupportsTextureRange())
{
FOpenGL::TextureRange(Target, 0, 0);
glTexParameteri(Target, GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_PRIVATE_APPLE);
}
// Leave bAllocatedStorage as false, so that the client storage buffers are setup only when the texture is locked
}
// Try to allocate using TexStorage2D
else if( FOpenGL::TexStorage2D( Target, NumMips, GLFormat.SizedInternalFormat[bSRGB], SizeX, SizeY, GLFormat.Format, GLFormat.Type, Flags) )
{
bAllocatedStorage = true;
}
else if (!GLFormat.bCompressed)
{
// Otherwise, allocate storage for each mip using TexImage2D
// We can't do so for compressed textures because we can't pass NULL in to CompressedTexImage2D!
bAllocatedStorage = true;
const bool bIsCubeTexture = Target == GL_TEXTURE_CUBE_MAP;
const GLenum FirstTarget = bIsCubeTexture ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : Target;
const uint32 NumTargets = bIsCubeTexture ? 6 : 1;
for(uint32 MipIndex = 0; MipIndex < uint32(NumMips); MipIndex++)
{
for(uint32 TargetIndex = 0; TargetIndex < NumTargets; TargetIndex++)
{
glTexImage2D(
FirstTarget + TargetIndex,
MipIndex,
GLFormat.InternalFormat[bSRGB],
FMath::Max<uint32>(1,(SizeX >> MipIndex)),
FMath::Max<uint32>(1,(SizeY >> MipIndex)),
0,
GLFormat.Format,
GLFormat.Type,
NULL
);
}
}
}
}
if (BulkData != NULL)
{
uint8* Data = (uint8*)BulkData->GetResourceBulkData();
uint32 MipOffset = 0;
const uint32 BlockSizeX = GPixelFormats[Format].BlockSizeX;
const uint32 BlockSizeY = GPixelFormats[Format].BlockSizeY;
for(uint32 MipIndex = 0; MipIndex < NumMips; MipIndex++)
{
uint32 NumBlocksX = AlignArbitrary(FMath::Max<uint32>(1,(SizeX >> MipIndex)), BlockSizeX) / BlockSizeX;
uint32 NumBlocksY = AlignArbitrary(FMath::Max<uint32>(1,(SizeY >> MipIndex)), BlockSizeY) / BlockSizeY;
uint32 NumLayers = FMath::Max<uint32>(1,ArraySize);
if(bArrayTexture )
{
if(bCubeTexture)
{
check(FOpenGL::SupportsTexture3D());
FOpenGL::TexSubImage3D(
/*Target=*/ Target,
/*Level=*/ MipIndex,
/* XOffset */ 0,
/* YOffset */ 0,
/* ZOffset */ 0,
/*SizeX=*/ FMath::Max<uint32>(1,(SizeX >> MipIndex)),
/*SizeY=*/ FMath::Max<uint32>(1,(SizeY >> MipIndex)),
/*SizeZ=*/ ArraySize,
/*Format=*/ GLFormat.Format,
/*Type=*/ GLFormat.Type,
/*Data=*/ &Data[MipOffset]
);
}
else
{
// @todo: refactor 2d texture arrays here?
check(!bCubeTexture);
}
MipOffset += NumBlocksX * NumBlocksY * NumLayers * GPixelFormats[Format].BlockBytes;
}
else
{
GLenum FirstTarget = bCubeTexture ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : Target;
uint32 NumTargets = bCubeTexture ? 6 : 1;
for(uint32 TargetIndex = 0; TargetIndex < NumTargets; TargetIndex++)
{
glTexSubImage2D(
/*Target=*/ FirstTarget + TargetIndex,
/*Level=*/ MipIndex,
/*XOffset*/ 0,
/*YOffset*/ 0,
/*SizeX=*/ FMath::Max<uint32>(1,(SizeX >> MipIndex)),
/*SizeY=*/ FMath::Max<uint32>(1,(SizeY >> MipIndex)),
/*Format=*/ GLFormat.Format,
/*Type=*/ GLFormat.Type,
/*Data=*/ &Data[MipOffset]
);
MipOffset += NumBlocksX * NumBlocksY * NumLayers * GPixelFormats[Format].BlockBytes;
}
}
}
}
}
else
{
check( FOpenGL::SupportsMultisampledTextures() );
check( BulkData == NULL);
// Try to create an immutable texture and fallback if it fails
if (!FOpenGL::TexStorage2DMultisample( Target, NumSamples, GLFormat.InternalFormat[bSRGB], SizeX, SizeY, true))
{
FOpenGL::TexImage2DMultisample(
Target,
NumSamples,
GLFormat.InternalFormat[bSRGB],
SizeX,
SizeY,
true
);
}
}
// Determine the attachment point for the texture.
GLenum Attachment = GL_NONE;
if((Flags & TexCreate_RenderTargetable) || (Flags & TexCreate_CPUReadback))
{
Attachment = GL_COLOR_ATTACHMENT0;
}
else if(Flags & TexCreate_DepthStencilTargetable)
{
Attachment = (Format == PF_DepthStencil && FOpenGL::SupportsPackedDepthStencil()) ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT;
}
else if(Flags & TexCreate_ResolveTargetable)
{
Attachment = (Format == PF_DepthStencil && FOpenGL::SupportsPackedDepthStencil())
? GL_DEPTH_STENCIL_ATTACHMENT
: ((Format == PF_ShadowDepth || Format == PF_D24)
? GL_DEPTH_ATTACHMENT
: GL_COLOR_ATTACHMENT0);
}
switch(Attachment)
{
case GL_COLOR_ATTACHMENT0:
check(GMaxOpenGLColorSamples>=(GLint)NumSamples);
break;
case GL_DEPTH_ATTACHMENT:
case GL_DEPTH_STENCIL_ATTACHMENT:
check(GMaxOpenGLDepthSamples>=(GLint)NumSamples);
break;
default:
break;
}
// @todo: If integer pixel format
//check(GMaxOpenGLIntegerSamples>=NumSamples);
FRHITexture* Texture;
if (bCubeTexture)
{
FOpenGLTextureCube* TextureCube = new FOpenGLTextureCube(this, TextureID, Target, Attachment, SizeX, SizeY, 0, NumMips, 1, ArraySize, (EPixelFormat)Format, true, bAllocatedStorage, Flags, TextureRange, InClearValue);
Texture = TextureCube;
}
else
{
FOpenGLTexture2D* Texture2D = new FOpenGLTexture2D(this,TextureID,Target,Attachment,SizeX,SizeY,0,NumMips,NumSamples, 1, (EPixelFormat)Format,false,bAllocatedStorage,Flags,TextureRange, InClearValue);
Texture = Texture2D;
}
OpenGLTextureAllocated( Texture, Flags );
// No need to restore texture stage; leave it like this,
// and the next draw will take care of cleaning it up; or
// next operation that needs the stage will switch something else in on it.
return Texture;
}
#if PLATFORM_MAC || PLATFORM_ANDROIDESDEFERRED // Flithy hack to workaround radr://16011763
GLuint FOpenGLTextureBase::GetOpenGLFramebuffer(uint32 ArrayIndices, uint32 MipmapLevels)
{
GLuint FBO = 0;
switch(Attachment)
{
case GL_COLOR_ATTACHMENT0:
{
FOpenGLTextureBase* RenderTarget[] = {this};
FBO = OpenGLRHI->GetOpenGLFramebuffer(1, RenderTarget, &ArrayIndices, &MipmapLevels, NULL);
break;
}
case GL_DEPTH_ATTACHMENT:
case GL_DEPTH_STENCIL_ATTACHMENT:
{
FBO = OpenGLRHI->GetOpenGLFramebuffer(1, NULL, &ArrayIndices, &MipmapLevels, this);
break;
}
default:
break;
}
return FBO;
}
#endif
void FOpenGLTextureBase::InvalidateTextureResourceInCache()
{
OpenGLRHI->InvalidateTextureResourceInCache(Resource);
if (SRVResource)
{
OpenGLRHI->InvalidateTextureResourceInCache(SRVResource);
}
}
template<typename RHIResourceType>
void TOpenGLTexture<RHIResourceType>::Resolve(uint32 MipIndex,uint32 ArrayIndex)
{
VERIFY_GL_SCOPE();
#if UE_BUILD_DEBUG
if((FOpenGLTexture2D*)this->GetTexture2D())
{
check( ((FOpenGLTexture2D*)this->GetTexture2D())->GetNumSamples() == 1 );
}
#endif
// Calculate the dimensions of the mip-map.
EPixelFormat PixelFormat = this->GetFormat();
const uint32 BlockSizeX = GPixelFormats[PixelFormat].BlockSizeX;
const uint32 BlockSizeY = GPixelFormats[PixelFormat].BlockSizeY;
const uint32 BlockBytes = GPixelFormats[PixelFormat].BlockBytes;
const uint32 MipSizeX = FMath::Max(this->GetSizeX() >> MipIndex,BlockSizeX);
const uint32 MipSizeY = FMath::Max(this->GetSizeY() >> MipIndex,BlockSizeY);
uint32 NumBlocksX = (MipSizeX + BlockSizeX - 1) / BlockSizeX;
uint32 NumBlocksY = (MipSizeY + BlockSizeY - 1) / BlockSizeY;
if ( PixelFormat == PF_PVRTC2 || PixelFormat == PF_PVRTC4 )
{
// PVRTC has minimum 2 blocks width and height
NumBlocksX = FMath::Max<uint32>(NumBlocksX, 2);
NumBlocksY = FMath::Max<uint32>(NumBlocksY, 2);
}
const uint32 MipBytes = NumBlocksX * NumBlocksY * BlockBytes;
const int32 BufferIndex = MipIndex * (bCubemap ? 6 : 1) * this->GetEffectiveSizeZ() + ArrayIndex;
// Standard path with a PBO mirroring ever slice of a texture to allow multiple simulataneous maps
if (!IsValidRef(PixelBuffers[BufferIndex]))
{
PixelBuffers[BufferIndex] = new FOpenGLPixelBuffer(0, MipBytes, BUF_Dynamic);
}
TRefCountPtr<FOpenGLPixelBuffer> PixelBuffer = PixelBuffers[BufferIndex];
check(PixelBuffer->GetSize() == MipBytes);
check(!PixelBuffer->IsLocked());
check( FOpenGL::SupportsPixelBuffers() );
// Transfer data from texture to pixel buffer.
// This may be further optimized by caching information if surface content was changed since last lock.
const FOpenGLTextureFormat& GLFormat = GOpenGLTextureFormats[PixelFormat];
const bool bSRGB = (this->GetFlags() & TexCreate_SRGB) != 0;
// Use a texture stage that's not likely to be used for draws, to avoid waiting
FOpenGLContextState& ContextState = OpenGLRHI->GetContextStateForCurrentContext();
OpenGLRHI->CachedSetupTextureStage(ContextState, FOpenGL::GetMaxCombinedTextureImageUnits() - 1, Target, Resource, -1, this->GetNumMips());
glBindBuffer( GL_PIXEL_PACK_BUFFER, PixelBuffer->Resource );
#if PLATFORM_MAC || PLATFORM_ANDROIDESDEFERRED // glReadPixels is async with PBOs - glGetTexImage is not: radr://16011763
if(Attachment == GL_COLOR_ATTACHMENT0 && !GLFormat.bCompressed)
{
GLuint SourceFBO = GetOpenGLFramebuffer(ArrayIndex, MipIndex);
check(SourceFBO > 0);
glBindFramebuffer(UGL_READ_FRAMEBUFFER, SourceFBO);
FOpenGL::ReadBuffer(Attachment);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, 0, MipSizeX, MipSizeY, GLFormat.Format, GLFormat.Type, 0 );
glPixelStorei(GL_PACK_ALIGNMENT, 4);
ContextState.Framebuffer = (GLuint)-1;
}
else
#endif
{
if( this->GetSizeZ() )
{
// apparently it's not possible to retrieve compressed image from GL_TEXTURE_2D_ARRAY in OpenGL for compressed images
// and for uncompressed ones it's not possible to specify the image index
check(0);
}
else
{
if (GLFormat.bCompressed)
{
FOpenGL::GetCompressedTexImage(
bCubemap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + ArrayIndex : Target,
MipIndex,
0); // offset into PBO
}
else
{
glPixelStorei(GL_PACK_ALIGNMENT, 1);
FOpenGL::GetTexImage(
bCubemap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + ArrayIndex : Target,
MipIndex,
GLFormat.Format,
GLFormat.Type,
0); // offset into PBO
glPixelStorei(GL_PACK_ALIGNMENT, 4);
}
}
}
glBindBuffer( GL_PIXEL_PACK_BUFFER, 0 );
// No need to restore texture stage; leave it like this,
// and the next draw will take care of cleaning it up; or
// next operation that needs the stage will switch something else in on it.
}
template<typename RHIResourceType>
void* TOpenGLTexture<RHIResourceType>::Lock(uint32 InMipIndex,uint32 ArrayIndex,EResourceLockMode LockMode,uint32& DestStride)
{
VERIFY_GL_SCOPE();
#if UE_BUILD_DEBUG
if((FOpenGLTexture2D*)this->GetTexture2D())
{
check( ((FOpenGLTexture2D*)this->GetTexture2D())->GetNumSamples() == 1 );
}
#endif
SCOPE_CYCLE_COUNTER(STAT_OpenGLLockTextureTime);
void* result = NULL;
// Calculate the dimensions of the mip-map.
EPixelFormat PixelFormat = this->GetFormat();
const uint32 BlockSizeX = GPixelFormats[PixelFormat].BlockSizeX;
const uint32 BlockSizeY = GPixelFormats[PixelFormat].BlockSizeY;
const uint32 BlockBytes = GPixelFormats[PixelFormat].BlockBytes;
const uint32 MipSizeX = FMath::Max(this->GetSizeX() >> InMipIndex,BlockSizeX);
const uint32 MipSizeY = FMath::Max(this->GetSizeY() >> InMipIndex,BlockSizeY);
uint32 NumBlocksX = (MipSizeX + BlockSizeX - 1) / BlockSizeX;
uint32 NumBlocksY = (MipSizeY + BlockSizeY - 1) / BlockSizeY;
if ( PixelFormat == PF_PVRTC2 || PixelFormat == PF_PVRTC4 )
{
// PVRTC has minimum 2 blocks width and height
NumBlocksX = FMath::Max<uint32>(NumBlocksX, 2);
NumBlocksY = FMath::Max<uint32>(NumBlocksY, 2);
}
const uint32 MipBytes = NumBlocksX * NumBlocksY * BlockBytes;
DestStride = NumBlocksX * BlockBytes;
const int32 BufferIndex = InMipIndex * (bCubemap ? 6 : 1) * this->GetEffectiveSizeZ() + ArrayIndex;
// Should we use client-storage to improve update time on platforms that require it
const FOpenGLTextureFormat& GLFormat = GOpenGLTextureFormats[PixelFormat];
bool const bRenderable = (this->GetFlags() & (TexCreate_RenderTargetable|TexCreate_ResolveTargetable|TexCreate_DepthStencilTargetable|TexCreate_CPUReadback)) != 0;
bool const bUseClientStorage = FOpenGL::SupportsClientStorage() && !FOpenGL::SupportsTextureView() && !bRenderable && !this->GetSizeZ() && !GLFormat.bCompressed;
if(!bUseClientStorage)
{
// Standard path with a PBO mirroring ever slice of a texture to allow multiple simulataneous maps
bool bBufferExists = true;
if (!IsValidRef(PixelBuffers[BufferIndex]))
{
bBufferExists = false;
PixelBuffers[BufferIndex] = new FOpenGLPixelBuffer(0, MipBytes, BUF_Dynamic);
}
TRefCountPtr<FOpenGLPixelBuffer> PixelBuffer = PixelBuffers[BufferIndex];
check(PixelBuffer->GetSize() == MipBytes);
check(!PixelBuffer->IsLocked());
// If the buffer already exists & the flags are such that the texture cannot be rendered to & is CPU accessible then we can skip the internal resolve for read locks. This makes HZB occlusion faster.
const bool bCPUTexResolved = bBufferExists && (this->GetFlags() & TexCreate_CPUReadback) && !(this->GetFlags() & (TexCreate_RenderTargetable|TexCreate_DepthStencilTargetable));
if( LockMode != RLM_WriteOnly && !bCPUTexResolved && FOpenGL::SupportsPixelBuffers() )
{
Resolve(InMipIndex, ArrayIndex);
}
result = PixelBuffer->Lock(0, PixelBuffer->GetSize(), LockMode == RLM_ReadOnly, LockMode != RLM_ReadOnly);
}
else
{
// Use APPLE_client_storage to reduce memory usage and improve performance
// GL's which support this extension only need copy a pointer, not the memory contents
check( FOpenGL::SupportsClientStorage() && !FOpenGL::SupportsTextureView() );
if(GetAllocatedStorageForMip(InMipIndex,ArrayIndex))
{
result = ClientStorageBuffers[BufferIndex].Data;
}
else
{
// The assumption at present is that this only applies to 2D & cubemap textures
// Array, 3D and variants thereof aren't supported.
const bool bIsCubeTexture = Target == GL_TEXTURE_CUBE_MAP;
const GLenum FirstTarget = bIsCubeTexture ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : Target;
const uint32 NumTargets = bIsCubeTexture ? 6 : 1;
uint8* MipPointer = TextureRange;
for(uint32 MipIndex = 0; MipIndex < FOpenGLTextureBase::NumMips; MipIndex++)
{
const uint32 MipSize = CalcTextureMipMapSize(this->GetSizeX(), this->GetSizeY(), PixelFormat, MipIndex);
for(uint32 TargetIndex = 0; TargetIndex < NumTargets; TargetIndex++)
{
const int32 ClientIndex = (MipIndex * NumTargets) + TargetIndex;
ClientStorageBuffers[ClientIndex].Data = MipPointer;
ClientStorageBuffers[ClientIndex].Size = MipSize;
ClientStorageBuffers[ClientIndex].bReadOnly = false;
MipPointer += MipSize;
SetAllocatedStorageForMip(MipIndex,TargetIndex);
}
}
result = ClientStorageBuffers[BufferIndex].Data;
}
ClientStorageBuffers[BufferIndex].bReadOnly = (LockMode == RLM_ReadOnly);
}
return result;
}
// Copied from OpenGLDebugFrameDump.
inline uint32 HalfFloatToFloatInteger(uint16 HalfFloat)
{
uint32 Sign = (HalfFloat >> 15) & 0x00000001;
uint32 Exponent = (HalfFloat >> 10) & 0x0000001f;
uint32 Mantiss = HalfFloat & 0x000003ff;
if (Exponent == 0)
{
if (Mantiss == 0) // Plus or minus zero
{
return Sign << 31;
}
else // Denormalized number -- renormalize it
{
while ((Mantiss & 0x00000400) == 0)
{
Mantiss <<= 1;
Exponent -= 1;
}
Exponent += 1;
Mantiss &= ~0x00000400;
}
}
else if (Exponent == 31)
{
if (Mantiss == 0) // Inf
return (Sign << 31) | 0x7f800000;
else // NaN
return (Sign << 31) | 0x7f800000 | (Mantiss << 13);
}
Exponent = Exponent + (127 - 15);
Mantiss = Mantiss << 13;
return (Sign << 31) | (Exponent << 23) | Mantiss;
}
inline float HalfFloatToFloat(uint16 HalfFloat)
{
union
{
float F;
uint32 I;
} Convert;
Convert.I = HalfFloatToFloatInteger(HalfFloat);
return Convert.F;
}
template<typename RHIResourceType>
void TOpenGLTexture<RHIResourceType>::Unlock(uint32 MipIndex,uint32 ArrayIndex)
{
VERIFY_GL_SCOPE();
SCOPE_CYCLE_COUNTER(STAT_OpenGLUnlockTextureTime);
const int32 BufferIndex = MipIndex * (bCubemap ? 6 : 1) * this->GetEffectiveSizeZ() + ArrayIndex;
TRefCountPtr<FOpenGLPixelBuffer> PixelBuffer = PixelBuffers[BufferIndex];
const FOpenGLTextureFormat& GLFormat = GOpenGLTextureFormats[this->GetFormat()];
const bool bSRGB = (this->GetFlags() & TexCreate_SRGB) != 0;
// Should we use client-storage to improve update time on platforms that require it
bool const bRenderable = (this->GetFlags() & (TexCreate_RenderTargetable|TexCreate_ResolveTargetable|TexCreate_DepthStencilTargetable|TexCreate_CPUReadback)) != 0;
bool const bUseClientStorage = FOpenGL::SupportsClientStorage() && !FOpenGL::SupportsTextureView() && !bRenderable && !this->GetSizeZ() && !GLFormat.bCompressed;
check(bUseClientStorage || IsValidRef(PixelBuffers[BufferIndex]));
#if PLATFORM_ANDROID
// check for FloatRGBA to RGBA8 conversion needed
if (this->GetFormat() == PF_FloatRGBA && GLFormat.Type == GL_UNSIGNED_BYTE)
{
UE_LOG(LogRHI, Warning, TEXT("Converting texture from PF_FloatRGBA to RGBA8! Only supported for limited cases of 0.0 to 1.0 values (clamped)"));
// Code path for non-PBO: and always uncompressed!
// Volume/array textures are currently only supported if PixelBufferObjects are also supported.
check(this->GetSizeZ() == 0);
// Use a texture stage that's not likely to be used for draws, to avoid waiting
FOpenGLContextState& ContextState = OpenGLRHI->GetContextStateForCurrentContext();
OpenGLRHI->CachedSetupTextureStage(ContextState, FOpenGL::GetMaxCombinedTextureImageUnits() - 1, Target, Resource, -1, this->GetNumMips());
CachedBindPixelUnpackBuffer(0);
// get the source data and size
uint16* floatData = (uint16*)PixelBuffer->GetLockedBuffer();
int32 texWidth = FMath::Max<uint32>(1, (this->GetSizeX() >> MipIndex));
int32 texHeight = FMath::Max<uint32>(1, (this->GetSizeY() >> MipIndex));
// always RGBA8 so 4 bytes / pixel
int nValues = texWidth * texHeight * 4;
uint8* rgbaData = (uint8*)FMemory::Malloc(nValues);
// convert to GL_BYTE (saturate)
uint8* outPtr = rgbaData;
while (nValues--)
{
int32 pixelValue = (int32)(HalfFloatToFloat(*floatData++) * 255.0f);
*outPtr++ = (uint8)(pixelValue < 0 ? 0 : (pixelValue < 256 ? pixelValue : 255));
}
// All construction paths should have called TexStorage2D or TexImage2D. So we will
// always call TexSubImage2D.
check(GetAllocatedStorageForMip(MipIndex, ArrayIndex) == true);
glTexSubImage2D(
bCubemap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + ArrayIndex : Target,
MipIndex,
0,
0,
texWidth,
texHeight,
GLFormat.Format,
GLFormat.Type,
rgbaData);
// free temporary conversion buffer
FMemory::Free(rgbaData);
// Unlock "PixelBuffer" and free the temp memory after the texture upload.
PixelBuffer->Unlock();
// No need to restore texture stage; leave it like this,
// and the next draw will take care of cleaning it up; or
// next operation that needs the stage will switch something else in on it.
CachedBindPixelUnpackBuffer(0);
return;
}
#endif
if ( !bUseClientStorage && FOpenGL::SupportsPixelBuffers() )
{
// Code path for PBO per slice
check(IsValidRef(PixelBuffers[BufferIndex]));
PixelBuffer->Unlock();
// Modify permission?
if (!PixelBuffer->IsLockReadOnly())
{
// Use a texture stage that's not likely to be used for draws, to avoid waiting
FOpenGLContextState& ContextState = OpenGLRHI->GetContextStateForCurrentContext();
OpenGLRHI->CachedSetupTextureStage(ContextState, FOpenGL::GetMaxCombinedTextureImageUnits() - 1, Target, Resource, -1, this->GetNumMips());
if( this->GetSizeZ() )
{
// texture 2D array
if (GLFormat.bCompressed)
{
FOpenGL::CompressedTexSubImage3D(
Target,
MipIndex,
0,
0,
ArrayIndex,
FMath::Max<uint32>(1,(this->GetSizeX() >> MipIndex)),
FMath::Max<uint32>(1,(this->GetSizeY() >> MipIndex)),
1,
GLFormat.InternalFormat[bSRGB],
PixelBuffer->GetSize(),
0);
}
else
{
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
check( FOpenGL::SupportsTexture3D() );
FOpenGL::TexSubImage3D(
Target,
MipIndex,
0,
0,
ArrayIndex,
FMath::Max<uint32>(1,(this->GetSizeX() >> MipIndex)),
FMath::Max<uint32>(1,(this->GetSizeY() >> MipIndex)),
1,
GLFormat.Format,
GLFormat.Type,
0); // offset into PBO
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
}
}
else
{
if (GLFormat.bCompressed)
{
if (GetAllocatedStorageForMip(MipIndex,ArrayIndex))
{
glCompressedTexSubImage2D(
bCubemap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + ArrayIndex : Target,
MipIndex,
0,
0,
FMath::Max<uint32>(1,(this->GetSizeX() >> MipIndex)),
FMath::Max<uint32>(1,(this->GetSizeY() >> MipIndex)),
GLFormat.InternalFormat[bSRGB],
PixelBuffer->GetSize(),
0); // offset into PBO
}
else
{
glCompressedTexImage2D(
bCubemap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + ArrayIndex : Target,
MipIndex,
GLFormat.InternalFormat[bSRGB],
FMath::Max<uint32>(1,(this->GetSizeX() >> MipIndex)),
FMath::Max<uint32>(1,(this->GetSizeY() >> MipIndex)),
0,
PixelBuffer->GetSize(),
0); // offset into PBO
SetAllocatedStorageForMip(MipIndex,ArrayIndex);
}
}
else
{
// All construction paths should have called TexStorage2D or TexImage2D. So we will
// always call TexSubImage2D.
check(GetAllocatedStorageForMip(MipIndex,ArrayIndex) == true);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexSubImage2D(
bCubemap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + ArrayIndex : Target,
MipIndex,
0,
0,
FMath::Max<uint32>(1,(this->GetSizeX() >> MipIndex)),
FMath::Max<uint32>(1,(this->GetSizeY() >> MipIndex)),
GLFormat.Format,
GLFormat.Type,
0); // offset into PBO
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
}
}
}
//need to free PBO if we aren't keeping shadow copies
if(!PLATFORM_MAC || !GLFormat.bCompressed || Target != GL_TEXTURE_2D)
{
PixelBuffers[BufferIndex] = NULL;
}
}
else if(!bUseClientStorage || !ClientStorageBuffers[BufferIndex].bReadOnly)
{
// Code path for non-PBO:
// Volume/array textures are currently only supported if PixelBufferObjects are also supported.
check(this->GetSizeZ() == 0);
// Use a texture stage that's not likely to be used for draws, to avoid waiting
FOpenGLContextState& ContextState = OpenGLRHI->GetContextStateForCurrentContext();
OpenGLRHI->CachedSetupTextureStage(ContextState, FOpenGL::GetMaxCombinedTextureImageUnits() - 1, Target, Resource, -1, this->GetNumMips());
CachedBindPixelUnpackBuffer( 0 );
uint32 LockedSize = 0;
void* LockedBuffer = nullptr;
if(bUseClientStorage)
{
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
LockedSize = ClientStorageBuffers[BufferIndex].Size;
LockedBuffer = ClientStorageBuffers[BufferIndex].Data;
}
else
{
LockedSize = PixelBuffer->GetSize();
LockedBuffer = PixelBuffer->GetLockedBuffer();
}
if (GLFormat.bCompressed)
{
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
if (GetAllocatedStorageForMip(MipIndex,ArrayIndex))
{
glCompressedTexSubImage2D(
bCubemap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + ArrayIndex : Target,
MipIndex,
0,
0,
FMath::Max<uint32>(1,(this->GetSizeX() >> MipIndex)),
FMath::Max<uint32>(1,(this->GetSizeY() >> MipIndex)),
GLFormat.InternalFormat[bSRGB],
LockedSize,
LockedBuffer);
}
else
{
glCompressedTexImage2D(
bCubemap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + ArrayIndex : Target,
MipIndex,
GLFormat.InternalFormat[bSRGB],
FMath::Max<uint32>(1,(this->GetSizeX() >> MipIndex)),
FMath::Max<uint32>(1,(this->GetSizeY() >> MipIndex)),
0,
LockedSize,
LockedBuffer);
SetAllocatedStorageForMip(MipIndex,ArrayIndex);
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
}
else
{
if (GetAllocatedStorageForMip(MipIndex,ArrayIndex))
{
glTexSubImage2D(
bCubemap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + ArrayIndex : Target,
MipIndex,
0,
0,
FMath::Max<uint32>(1,(this->GetSizeX() >> MipIndex)),
FMath::Max<uint32>(1,(this->GetSizeY() >> MipIndex)),
GLFormat.Format,
GLFormat.Type,
LockedBuffer);
}
else
{
glTexImage2D(
bCubemap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + ArrayIndex : Target,
MipIndex,
GLFormat.InternalFormat[bSRGB],
FMath::Max<uint32>(1,(this->GetSizeX() >> MipIndex)),
FMath::Max<uint32>(1,(this->GetSizeY() >> MipIndex)),
0,
GLFormat.Format,
GLFormat.Type,
LockedBuffer);
SetAllocatedStorageForMip(MipIndex,ArrayIndex);
}
}
if(bUseClientStorage)
{
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
}
else
{
// Unlock "PixelBuffer" and free the temp memory after the texture upload.
PixelBuffer->Unlock();
}
}
// No need to restore texture stage; leave it like this,
// and the next draw will take care of cleaning it up; or
// next operation that needs the stage will switch something else in on it.
CachedBindPixelUnpackBuffer(0);
}
template<typename RHIResourceType>
void TOpenGLTexture<RHIResourceType>::CloneViaCopyImage( TOpenGLTexture<RHIResourceType>* Src, uint32 NumMips, int32 SrcOffset, int32 DstOffset)
{
VERIFY_GL_SCOPE();
check(FOpenGL::SupportsCopyImage());
for (uint32 ArrayIndex = 0; ArrayIndex < this->GetEffectiveSizeZ(); ArrayIndex++)
{
// use the Copy Image functionality to copy mip level by mip level
for(uint32 MipIndex = 0;MipIndex < NumMips;++MipIndex)
{
// Calculate the dimensions of the mip-map.
const uint32 DstMipIndex = MipIndex + DstOffset;
const uint32 SrcMipIndex = MipIndex + SrcOffset;
const uint32 MipSizeX = FMath::Max(this->GetSizeX() >> DstMipIndex,uint32(1));
const uint32 MipSizeY = FMath::Max(this->GetSizeY() >> DstMipIndex,uint32(1));
if(FOpenGL::AmdWorkaround() && ((MipSizeX < 4) || (MipSizeY < 4))) break;
// copy the texture data
FOpenGL::CopyImageSubData( Src->Resource, Src->Target, SrcMipIndex, 0, 0, ArrayIndex,
Resource, Target, DstMipIndex, 0, 0, ArrayIndex, MipSizeX, MipSizeY, 1);
}
}
}
template<typename RHIResourceType>
void TOpenGLTexture<RHIResourceType>::CloneViaPBO( TOpenGLTexture<RHIResourceType>* Src, uint32 NumMips, int32 SrcOffset, int32 DstOffset)
{
VERIFY_GL_SCOPE();
// apparently it's not possible to retrieve compressed image from GL_TEXTURE_2D_ARRAY in OpenGL for compressed images
// and for uncompressed ones it's not possible to specify the image index
check(this->GetSizeZ() == 0);
// only PBO path is supported here
check( FOpenGL::SupportsPixelBuffers() );
EPixelFormat PixelFormat = this->GetFormat();
check(PixelFormat == Src->GetFormat());
const FOpenGLTextureFormat& GLFormat = GOpenGLTextureFormats[PixelFormat];
const bool bSRGB = (this->GetFlags() & TexCreate_SRGB) != 0;
check(bSRGB == ((Src->GetFlags() & TexCreate_SRGB) != 0));
const uint32 BlockSizeX = GPixelFormats[PixelFormat].BlockSizeX;
const uint32 BlockSizeY = GPixelFormats[PixelFormat].BlockSizeY;
const uint32 BlockBytes = GPixelFormats[PixelFormat].BlockBytes;
FOpenGLContextState& ContextState = OpenGLRHI->GetContextStateForCurrentContext();
for (uint32 ArrayIndex = 0; ArrayIndex < this->GetEffectiveSizeZ(); ArrayIndex++)
{
// use PBO functionality to copy mip level by mip level
for(uint32 MipIndex = 0;MipIndex < NumMips;++MipIndex)
{
// Actual mip levels
const uint32 DstMipIndex = MipIndex + DstOffset;
const uint32 SrcMipIndex = MipIndex + SrcOffset;
// Calculate the dimensions of the mip-map.
const uint32 MipSizeX = FMath::Max(this->GetSizeX() >> DstMipIndex,1u);
const uint32 MipSizeY = FMath::Max(this->GetSizeY() >> DstMipIndex,1u);
// Then the rounded PBO size required to capture this mip
const uint32 DataSizeX = FMath::Max(MipSizeX,BlockSizeX);
const uint32 DataSizeY = FMath::Max(MipSizeY,BlockSizeY);
uint32 NumBlocksX = (DataSizeX + BlockSizeX - 1) / BlockSizeX;
uint32 NumBlocksY = (DataSizeY + BlockSizeY - 1) / BlockSizeY;
if ( PixelFormat == PF_PVRTC2 || PixelFormat == PF_PVRTC4 )
{
// PVRTC has minimum 2 blocks width and height
NumBlocksX = FMath::Max<uint32>(NumBlocksX, 2);
NumBlocksY = FMath::Max<uint32>(NumBlocksY, 2);
}
const uint32 MipBytes = NumBlocksX * NumBlocksY * BlockBytes;
const int32 BufferIndex = DstMipIndex * (bCubemap ? 6 : 1) * this->GetEffectiveSizeZ() + ArrayIndex;
const int32 SrcBufferIndex = SrcMipIndex * (Src->bCubemap ? 6 : 1) * Src->GetEffectiveSizeZ() + ArrayIndex;
// Retain the existing PBO for this texture data - as it is compressed it won't change
if(PLATFORM_MAC && GLFormat.bCompressed && Target == GL_TEXTURE_2D)
{
PixelBuffers[BufferIndex] = Src->PixelBuffers[SrcBufferIndex];
check(PixelBuffers[BufferIndex]->GetSize() == MipBytes);
check(!PixelBuffers[BufferIndex]->IsLocked());
}
// Standard path with a PBO mirroring ever slice of a texture to allow multiple simulataneous maps
if (!IsValidRef(PixelBuffers[BufferIndex]))
{
PixelBuffers[BufferIndex] = new FOpenGLPixelBuffer(0, MipBytes, BUF_Dynamic);
}
TRefCountPtr<FOpenGLPixelBuffer> PixelBuffer = PixelBuffers[BufferIndex];
check(PixelBuffer->GetSize() == MipBytes);
check(!PixelBuffer->IsLocked());
// Transfer data from texture to pixel buffer.
// This may be further optimized by caching information if surface content was changed since last lock.
if(!PLATFORM_MAC || !GLFormat.bCompressed || !IsValidRef(Src->PixelBuffers[SrcBufferIndex]))
{
// Use a texture stage that's not likely to be used for draws, to avoid waiting
OpenGLRHI->CachedSetupTextureStage(ContextState, FOpenGL::GetMaxCombinedTextureImageUnits() - 1, Src->Target, Src->Resource, -1, this->GetNumMips());
glBindBuffer( GL_PIXEL_PACK_BUFFER, PixelBuffer->Resource );
#if PLATFORM_MAC || PLATFORM_ANDROIDESDEFERRED // glReadPixels is async with PBOs - glGetTexImage is not: radr://16011763
if(Attachment == GL_COLOR_ATTACHMENT0 && !GLFormat.bCompressed)
{
GLuint SourceFBO = Src->GetOpenGLFramebuffer(ArrayIndex, SrcMipIndex);
check(SourceFBO > 0);
glBindFramebuffer(UGL_READ_FRAMEBUFFER, SourceFBO);
FOpenGL::ReadBuffer(Attachment);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, 0, MipSizeX, MipSizeY, GLFormat.Format, GLFormat.Type, 0 );
glPixelStorei(GL_PACK_ALIGNMENT, 4);
ContextState.Framebuffer = (GLuint)-1;
}
else
#endif
if (GLFormat.bCompressed)
{
FOpenGL::GetCompressedTexImage(Src->bCubemap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + ArrayIndex : Src->Target,
SrcMipIndex,
0); // offset into PBO
}
else
{
glPixelStorei(GL_PACK_ALIGNMENT, 1);
FOpenGL::GetTexImage(Src->bCubemap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + ArrayIndex : Src->Target,
SrcMipIndex,
GLFormat.Format,
GLFormat.Type,
0); // offset into PBO
glPixelStorei(GL_PACK_ALIGNMENT, 4);
}
}
// copy the texture data
// Upload directly into Dst to avoid out-of-band synchronization caused by glMapBuffer!
{
CachedBindPixelUnpackBuffer( PixelBuffer->Resource );
// Use a texture stage that's not likely to be used for draws, to avoid waiting
OpenGLRHI->CachedSetupTextureStage(ContextState, FOpenGL::GetMaxCombinedTextureImageUnits() - 1, Target, Resource, -1, this->GetNumMips());
if( this->GetSizeZ() )
{
// texture 2D array
if (GLFormat.bCompressed)
{
FOpenGL::CompressedTexSubImage3D(Target,
DstMipIndex,
0,
0,
ArrayIndex,
MipSizeX,
MipSizeY,
1,
GLFormat.InternalFormat[bSRGB],
PixelBuffer->GetSize(),
0);
}
else
{
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
check( FOpenGL::SupportsTexture3D() );
FOpenGL::TexSubImage3D(Target,
DstMipIndex,
0,
0,
ArrayIndex,
MipSizeX,
MipSizeY,
1,
GLFormat.Format,
GLFormat.Type,
0); // offset into PBO
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
}
}
else
{
if (GLFormat.bCompressed)
{
if (GetAllocatedStorageForMip(DstMipIndex,ArrayIndex))
{
glCompressedTexSubImage2D(bCubemap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + ArrayIndex : Target,
DstMipIndex,
0,
0,
MipSizeX,
MipSizeY,
GLFormat.InternalFormat[bSRGB],
PixelBuffer->GetSize(),
0); // offset into PBO
}
else
{
glCompressedTexImage2D(bCubemap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + ArrayIndex : Target,
DstMipIndex,
GLFormat.InternalFormat[bSRGB],
MipSizeX,
MipSizeY,
0,
PixelBuffer->GetSize(),
0); // offset into PBO
SetAllocatedStorageForMip(DstMipIndex,ArrayIndex);
}
}
else
{
// All construction paths should have called TexStorage2D or TexImage2D. So we will
// always call TexSubImage2D.
check(GetAllocatedStorageForMip(DstMipIndex,ArrayIndex) == true);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexSubImage2D(bCubemap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + ArrayIndex : Target,
DstMipIndex,
0,
0,
MipSizeX,
MipSizeY,
GLFormat.Format,
GLFormat.Type,
0); // offset into PBO
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
}
}
}
if(!PLATFORM_MAC || !GLFormat.bCompressed || Target != GL_TEXTURE_2D)
{
// need to free PBO if we aren't keeping shadow copies
PixelBuffers[BufferIndex] = NULL;
}
// No need to restore texture stage; leave it like this,
// and the next draw will take care of cleaning it up; or
// next operation that needs the stage will switch something else in on it.
}
}
// Reset the buffer bindings on exit only
glBindBuffer( GL_PIXEL_PACK_BUFFER, 0 );
CachedBindPixelUnpackBuffer(0);
}
/*-----------------------------------------------------------------------------
2D texture support.
-----------------------------------------------------------------------------*/
/**
* Creates a 2D RHI texture resource
* @param SizeX - width of the texture to create
* @param SizeY - height of the texture to create
* @param Format - EPixelFormat texture format
* @param NumMips - number of mips to generate or 0 for full mip pyramid
* @param Flags - ETextureCreateFlags creation flags
*/
FTexture2DRHIRef FOpenGLDynamicRHI::RHICreateTexture2D(uint32 SizeX,uint32 SizeY,uint8 Format,uint32 NumMips,uint32 NumSamples,uint32 Flags,FRHIResourceCreateInfo& Info)
{
return (FRHITexture2D*)CreateOpenGLTexture(SizeX,SizeY,false,false,Format,NumMips,NumSamples,1,Flags,Info.ClearValueBinding,Info.BulkData);
}
FTexture2DRHIRef FOpenGLDynamicRHI::RHIAsyncCreateTexture2D(uint32 SizeX, uint32 SizeY, uint8 Format, uint32 NumMips, uint32 Flags, void** InitialMipData, uint32 NumInitialMips)
{
check(0);
return FTexture2DRHIRef();
}
void FOpenGLDynamicRHI::RHICopySharedMips(FTexture2DRHIParamRef DestTexture2D,FTexture2DRHIParamRef SrcTexture2D)
{
check(0);
}
FTexture2DArrayRHIRef FOpenGLDynamicRHI::RHICreateTexture2DArray(uint32 SizeX,uint32 SizeY,uint32 SizeZ,uint8 Format,uint32 NumMips,uint32 Flags, FRHIResourceCreateInfo& Info)
{
VERIFY_GL_SCOPE();
SCOPE_CYCLE_COUNTER(STAT_OpenGLCreateTextureTime);
check( FOpenGL::SupportsTexture3D() );
if(NumMips == 0)
{
NumMips = FindMaxMipmapLevel(SizeX, SizeY);
}
if (GMaxRHIFeatureLevel <= ERHIFeatureLevel::ES3_1)
{
// Remove sRGB read flag when not supported
Flags &= ~TexCreate_SRGB;
}
GLuint TextureID = 0;
FOpenGL::GenTextures(1, &TextureID);
const GLenum Target = GL_TEXTURE_2D_ARRAY;
// Use a texture stage that's not likely to be used for draws, to avoid waiting
FOpenGLContextState& ContextState = GetContextStateForCurrentContext();
CachedSetupTextureStage(ContextState, FOpenGL::GetMaxCombinedTextureImageUnits() - 1, Target, TextureID, 0, NumMips);
glTexParameteri(Target, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(Target, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(Target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(Target, GL_TEXTURE_MIN_FILTER, NumMips > 1 ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST);
if( FOpenGL::SupportsTextureFilterAnisotropic() )
{
glTexParameteri(Target, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);
}
glTexParameteri(Target, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(Target, GL_TEXTURE_MAX_LEVEL, NumMips - 1);
TextureMipLimits.Add(TextureID, TPairInitializer<GLenum, GLenum>(0, NumMips - 1));
const bool bSRGB = (Flags&TexCreate_SRGB) != 0;
const FOpenGLTextureFormat& GLFormat = GOpenGLTextureFormats[Format];
if (GLFormat.InternalFormat[bSRGB] == GL_NONE)
{
UE_LOG(LogRHI, Fatal,TEXT("Texture format '%s' not supported."), GPixelFormats[Format].Name);
}
checkf(!GLFormat.bCompressed, TEXT("%s compressed 2D texture arrays not currently supported by the OpenGL RHI"), GPixelFormats[Format].Name);
// Make sure PBO is disabled
CachedBindPixelUnpackBuffer(ContextState, 0);
uint8* Data = Info.BulkData ? (uint8*)Info.BulkData->GetResourceBulkData() : NULL;
uint32 MipOffset = 0;
FOpenGL::TexStorage3D( Target, NumMips, GLFormat.InternalFormat[bSRGB], SizeX, SizeY, SizeZ, GLFormat.Format, GLFormat.Type );
if (Data)
{
for(uint32 MipIndex = 0; MipIndex < NumMips; MipIndex++)
{
FOpenGL::TexSubImage3D(
/*Target=*/ Target,
/*Level=*/ MipIndex,
0,
0,
0,
/*SizeX=*/ FMath::Max<uint32>(1,(SizeX >> MipIndex)),
/*SizeY=*/ FMath::Max<uint32>(1,(SizeY >> MipIndex)),
/*SizeZ=*/ SizeZ,
/*Format=*/ GLFormat.Format,
/*Type=*/ GLFormat.Type,
/*Data=*/ Data ? &Data[MipOffset] : NULL
);
uint32 SysMemPitch = FMath::Max<uint32>(1,SizeX >> MipIndex) * GPixelFormats[Format].BlockBytes;
uint32 SysMemSlicePitch = FMath::Max<uint32>(1,SizeY >> MipIndex) * SysMemPitch;
MipOffset += SizeZ * SysMemSlicePitch;
}
}
// Determine the attachment point for the texture.
GLenum Attachment = GL_NONE;
if(Flags & TexCreate_RenderTargetable)
{
Attachment = GL_COLOR_ATTACHMENT0;
}
else if(Flags & TexCreate_DepthStencilTargetable)
{
Attachment = (FOpenGL::SupportsPackedDepthStencil() && Format == PF_DepthStencil) ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT;
}
else if(Flags & TexCreate_ResolveTargetable)
{
Attachment = (Format == PF_DepthStencil && FOpenGL::SupportsPackedDepthStencil())
? GL_DEPTH_STENCIL_ATTACHMENT
: ((Format == PF_ShadowDepth || Format == PF_D24)
? GL_DEPTH_ATTACHMENT
: GL_COLOR_ATTACHMENT0);
}
FOpenGLTexture2DArray* Texture = new FOpenGLTexture2DArray(this,TextureID,Target,Attachment,SizeX,SizeY,SizeZ,NumMips,1, SizeZ, (EPixelFormat)Format,false,true,Flags,nullptr,Info.ClearValueBinding);
OpenGLTextureAllocated( Texture, Flags );
// No need to restore texture stage; leave it like this,
// and the next draw will take care of cleaning it up; or
// next operation that needs the stage will switch something else in on it.
return Texture;
}
FTexture3DRHIRef FOpenGLDynamicRHI::RHICreateTexture3D(uint32 SizeX,uint32 SizeY,uint32 SizeZ,uint8 Format,uint32 NumMips,uint32 Flags,FRHIResourceCreateInfo& CreateInfo)
{
VERIFY_GL_SCOPE();
SCOPE_CYCLE_COUNTER(STAT_OpenGLCreateTextureTime);
check( FOpenGL::SupportsTexture3D() );
if(NumMips == 0)
{
NumMips = FindMaxMipmapLevel(SizeX, SizeY, SizeZ);
}
if (GMaxRHIFeatureLevel <= ERHIFeatureLevel::ES3_1)
{
// Remove sRGB read flag when not supported
Flags &= ~TexCreate_SRGB;
}
GLuint TextureID = 0;
FOpenGL::GenTextures(1, &TextureID);
const GLenum Target = GL_TEXTURE_3D;
// Use a texture stage that's not likely to be used for draws, to avoid waiting
FOpenGLContextState& ContextState = GetContextStateForCurrentContext();
CachedSetupTextureStage(ContextState, FOpenGL::GetMaxCombinedTextureImageUnits() - 1, Target, TextureID, 0, NumMips);
glTexParameteri(Target, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(Target, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(Target, GL_TEXTURE_WRAP_R, GL_REPEAT);
glTexParameteri(Target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(Target, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
if( FOpenGL::SupportsTextureFilterAnisotropic() )
{
glTexParameteri(Target, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);
}
glTexParameteri(Target, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(Target, GL_TEXTURE_MAX_LEVEL, NumMips - 1);
TextureMipLimits.Add(TextureID, TPairInitializer<GLenum, GLenum>(0, NumMips - 1));
const bool bSRGB = (Flags&TexCreate_SRGB) != 0;
const FOpenGLTextureFormat& GLFormat = GOpenGLTextureFormats[Format];
if (GLFormat.InternalFormat[bSRGB] == GL_NONE)
{
UE_LOG(LogRHI, Fatal,TEXT("Texture format '%s' not supported."), GPixelFormats[Format].Name);
}
// Make sure PBO is disabled
CachedBindPixelUnpackBuffer(ContextState,0);
uint8* Data = CreateInfo.BulkData ? (uint8*)CreateInfo.BulkData->GetResourceBulkData() : NULL;
uint32 MipOffset = 0;
FOpenGL::TexStorage3D( Target, NumMips, GLFormat.InternalFormat[bSRGB], SizeX, SizeY, SizeZ, GLFormat.Format, GLFormat.Type );
if (Data)
{
for(uint32 MipIndex = 0; MipIndex < NumMips; MipIndex++)
{
FOpenGL::TexSubImage3D(
/*Target=*/ Target,
/*Level=*/ MipIndex,
0,
0,
0,
/*SizeX=*/ FMath::Max<uint32>(1,(SizeX >> MipIndex)),
/*SizeY=*/ FMath::Max<uint32>(1,(SizeY >> MipIndex)),
/*SizeZ=*/ FMath::Max<uint32>(1,(SizeZ >> MipIndex)),
/*Format=*/ GLFormat.Format,
/*Type=*/ GLFormat.Type,
/*Data=*/ Data ? &Data[MipOffset] : NULL
);
uint32 SysMemPitch = FMath::Max<uint32>(1,SizeX >> MipIndex) * GPixelFormats[Format].BlockBytes;
uint32 SysMemSlicePitch = FMath::Max<uint32>(1,SizeY >> MipIndex) * SysMemPitch;
MipOffset += FMath::Max<uint32>(1,SizeZ >> MipIndex) * SysMemSlicePitch;
}
}
// Determine the attachment point for the texture.
GLenum Attachment = GL_NONE;
if(Flags & TexCreate_RenderTargetable)
{
Attachment = GL_COLOR_ATTACHMENT0;
}
else if(Flags & TexCreate_DepthStencilTargetable)
{
Attachment = Format == PF_DepthStencil ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT;
}
else if(Flags & TexCreate_ResolveTargetable)
{
Attachment = (Format == PF_DepthStencil && FOpenGL::SupportsCombinedDepthStencilAttachment())
? GL_DEPTH_STENCIL_ATTACHMENT
: ((Format == PF_ShadowDepth || Format == PF_D24)
? GL_DEPTH_ATTACHMENT
: GL_COLOR_ATTACHMENT0);
}
FOpenGLTexture3D* Texture = new FOpenGLTexture3D(this,TextureID,Target,Attachment,SizeX,SizeY,SizeZ,NumMips,1,1, (EPixelFormat)Format,false,true,Flags,nullptr,CreateInfo.ClearValueBinding);
OpenGLTextureAllocated( Texture, Flags );
// No need to restore texture stage; leave it like this,
// and the next draw will take care of cleaning it up; or
// next operation that needs the stage will switch something else in on it.
return Texture;
}
void FOpenGLDynamicRHI::RHIGetResourceInfo(FTextureRHIParamRef Ref, FRHIResourceInfo& OutInfo)
{
}
FShaderResourceViewRHIRef FOpenGLDynamicRHI::RHICreateShaderResourceView(FTexture2DRHIParamRef Texture2DRHI, uint8 MipLevel)
{
FOpenGLTexture2D* Texture2D = ResourceCast(Texture2DRHI);
FOpenGLShaderResourceView *View = 0;
if (FOpenGL::SupportsTextureView())
{
VERIFY_GL_SCOPE();
GLuint Resource = 0;
FOpenGL::GenTextures( 1, &Resource);
const FOpenGLTextureFormat& GLFormat = GOpenGLTextureFormats[Texture2D->GetFormat()];
const bool bSRGB = (Texture2D->GetFlags()&TexCreate_SRGB) != 0;
FOpenGL::TextureView( Resource, Texture2D->Target, Texture2D->Resource, GLFormat.InternalFormat[bSRGB], MipLevel, 1, 0, 1);
View = new FOpenGLShaderResourceView(this, Resource, Texture2D->Target, MipLevel, true);
}
else
{
View = new FOpenGLShaderResourceView(this, Texture2D->Resource, Texture2D->Target, MipLevel, false);
}
FShaderCache::LogSRV(View, Texture2DRHI, MipLevel, 1, PF_Unknown);
return View;
}
FShaderResourceViewRHIRef FOpenGLDynamicRHI::RHICreateShaderResourceView(FTexture2DRHIParamRef Texture2DRHI, uint8 MipLevel, uint8 NumMipLevels, uint8 Format)
{
FOpenGLTexture2D* Texture2D = ResourceCast(Texture2DRHI);
FOpenGLShaderResourceView *View = 0;
if (FOpenGL::SupportsTextureView())
{
VERIFY_GL_SCOPE();
GLuint Resource = 0;
FOpenGL::GenTextures( 1, &Resource);
if (Format != PF_X24_G8)
{
const FOpenGLTextureFormat& GLFormat = GOpenGLTextureFormats[Format];
const bool bSRGB = (Texture2D->GetFlags()&TexCreate_SRGB) != 0;
FOpenGL::TextureView( Resource, Texture2D->Target, Texture2D->Resource, GLFormat.InternalFormat[bSRGB], MipLevel, NumMipLevels, 0, 1);
}
else
{
// PF_X24_G8 doesn't correspond to a real format under OpenGL
// The solution is to create a view with the original format, and convert it to return the stencil index
// To match component locations, texture swizzle needs to be setup too
const FOpenGLTextureFormat& GLFormat = GOpenGLTextureFormats[Texture2D->GetFormat()];
// create a second depth/stencil view
FOpenGL::TextureView( Resource, Texture2D->Target, Texture2D->Resource, GLFormat.InternalFormat[0], MipLevel, NumMipLevels, 0, 1);
// Use a texture stage that's not likely to be used for draws, to avoid waiting
FOpenGLContextState& ContextState = GetContextStateForCurrentContext();
CachedSetupTextureStage(ContextState, FOpenGL::GetMaxCombinedTextureImageUnits() - 1, Texture2D->Target, Resource, 0, NumMipLevels);
//set the texture to return the stencil index, and then force the components to match D3D
glTexParameteri( Texture2D->Target, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX);
glTexParameteri( Texture2D->Target, GL_TEXTURE_SWIZZLE_R, GL_ZERO);
glTexParameteri( Texture2D->Target, GL_TEXTURE_SWIZZLE_G, GL_RED);
glTexParameteri( Texture2D->Target, GL_TEXTURE_SWIZZLE_B, GL_ZERO);
glTexParameteri( Texture2D->Target, GL_TEXTURE_SWIZZLE_A, GL_ZERO);
}
View = new FOpenGLShaderResourceView(this, Resource, Texture2D->Target, MipLevel, true);
}
else
{
uint32 const Target = Texture2D->Target;
GLuint Resource = Texture2D->Resource;
FTexture2DRHIParamRef DepthStencilTex = nullptr;
// For stencil sampling we have to use a separate single channel texture to blit stencil data into
#if PLATFORM_DESKTOP || PLATFORM_ANDROIDESDEFERRED
if (FOpenGL::GetFeatureLevel() >= ERHIFeatureLevel::SM4 && Format == PF_X24_G8 && FOpenGL::SupportsPixelBuffers())
{
check(NumMipLevels == 1 && MipLevel == 0);
if (!Texture2D->SRVResource)
{
FOpenGL::GenTextures(1, &Texture2D->SRVResource);
GLenum const InternalFormat = GL_R8UI;
GLenum const ChannelFormat = GL_RED_INTEGER;
uint32 const SizeX = Texture2D->GetSizeX();
uint32 const SizeY = Texture2D->GetSizeY();
GLenum const Type = GL_UNSIGNED_BYTE;
uint32 const Flags = 0;
FOpenGLContextState& ContextState = GetContextStateForCurrentContext();
CachedSetupTextureStage(ContextState, FOpenGL::GetMaxCombinedTextureImageUnits() - 1, Target, Texture2D->SRVResource, MipLevel, NumMipLevels);
if (!FOpenGL::TexStorage2D(Target, NumMipLevels, InternalFormat, SizeX, SizeY, ChannelFormat, Type, Flags))
{
glTexImage2D(Target, 0, InternalFormat, SizeX, SizeY, 0, ChannelFormat, Type, nullptr);
}
TArray<uint8> ZeroData;
ZeroData.AddZeroed(SizeX * SizeY);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexSubImage2D(
Target,
0,
0,
0,
SizeX,
SizeY,
ChannelFormat,
Type,
ZeroData.GetData());
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
//set the texture to return the stencil index, and then force the components to match D3D
glTexParameteri( Target, GL_TEXTURE_SWIZZLE_R, GL_ZERO);
glTexParameteri( Target, GL_TEXTURE_SWIZZLE_G, GL_RED);
glTexParameteri( Target, GL_TEXTURE_SWIZZLE_B, GL_ZERO);
glTexParameteri( Target, GL_TEXTURE_SWIZZLE_A, GL_ZERO);
}
check(Texture2D->SRVResource);
Resource = Texture2D->SRVResource;
DepthStencilTex = Texture2DRHI;
}
#endif
View = new FOpenGLShaderResourceView(this, Resource, Target, MipLevel, false);
View->Texture2D = DepthStencilTex;
}
FShaderCache::LogSRV(View, Texture2DRHI, MipLevel, NumMipLevels, Format);
return View;
}
FShaderResourceViewRHIRef FOpenGLDynamicRHI::RHICreateShaderResourceView(FTexture3DRHIParamRef Texture3DRHI, uint8 MipLevel)
{
FOpenGLTexture3D* Texture3D = ResourceCast(Texture3DRHI);
FOpenGLShaderResourceView *View = 0;
if (FOpenGL::SupportsTextureView())
{
VERIFY_GL_SCOPE();
GLuint Resource = 0;
FOpenGL::GenTextures( 1, &Resource);
const FOpenGLTextureFormat& GLFormat = GOpenGLTextureFormats[Texture3D->GetFormat()];
const bool bSRGB = (Texture3D->GetFlags()&TexCreate_SRGB) != 0;
FOpenGL::TextureView( Resource, Texture3D->Target, Texture3D->Resource, GLFormat.InternalFormat[bSRGB], MipLevel, 1, 0, 1);
View = new FOpenGLShaderResourceView(this, Resource, Texture3D->Target, MipLevel, true);
}
else
{
View = new FOpenGLShaderResourceView(this, Texture3D->Resource, Texture3D->Target, MipLevel, false);
}
FShaderCache::LogSRV(View, Texture3DRHI, MipLevel, Texture3DRHI->GetNumMips(), Texture3DRHI->GetFormat());
return View;
}
FShaderResourceViewRHIRef FOpenGLDynamicRHI::RHICreateShaderResourceView(FTexture2DArrayRHIParamRef Texture2DArrayRHI, uint8 MipLevel)
{
FOpenGLTexture2DArray* Texture2DArray = ResourceCast(Texture2DArrayRHI);
FOpenGLShaderResourceView *View = 0;
if (FOpenGL::SupportsTextureView())
{
VERIFY_GL_SCOPE();
GLuint Resource = 0;
FOpenGL::GenTextures( 1, &Resource);
const FOpenGLTextureFormat& GLFormat = GOpenGLTextureFormats[Texture2DArray->GetFormat()];
const bool bSRGB = (Texture2DArray->GetFlags()&TexCreate_SRGB) != 0;
FOpenGL::TextureView( Resource, Texture2DArray->Target, Texture2DArray->Resource, GLFormat.InternalFormat[bSRGB], MipLevel, 1, 0, 1);
View = new FOpenGLShaderResourceView(this, Resource, Texture2DArray->Target, MipLevel, true);
}
else
{
View = new FOpenGLShaderResourceView(this, Texture2DArray->Resource, Texture2DArray->Target, MipLevel, false);
}
FShaderCache::LogSRV(View, Texture2DArrayRHI, MipLevel, Texture2DArrayRHI->GetNumMips(), Texture2DArrayRHI->GetFormat());
return View;
}
FShaderResourceViewRHIRef FOpenGLDynamicRHI::RHICreateShaderResourceView(FTextureCubeRHIParamRef TextureCubeRHI, uint8 MipLevel)
{
FOpenGLTextureCube* TextureCube = ResourceCast(TextureCubeRHI);
FOpenGLShaderResourceView *View = 0;
if (FOpenGL::SupportsTextureView())
{
VERIFY_GL_SCOPE();
GLuint Resource = 0;
FOpenGL::GenTextures( 1, &Resource);
const FOpenGLTextureFormat& GLFormat = GOpenGLTextureFormats[TextureCube->GetFormat()];
const bool bSRGB = (TextureCube->GetFlags()&TexCreate_SRGB) != 0;
FOpenGL::TextureView( Resource, TextureCube->Target, TextureCube->Resource, GLFormat.InternalFormat[bSRGB], MipLevel, 1, 0, 1);
View = new FOpenGLShaderResourceView(this, Resource, TextureCube->Target, MipLevel, true);
}
else
{
View = new FOpenGLShaderResourceView(this, TextureCube->Resource, TextureCube->Target, MipLevel, false);
}
FShaderCache::LogSRV(View, TextureCube, MipLevel, TextureCubeRHI->GetNumMips(), TextureCubeRHI->GetFormat());
return View;
}
/** Generates mip maps for the surface. */
void FOpenGLDynamicRHI::RHIGenerateMips(FTextureRHIParamRef SurfaceRHI)
{
VERIFY_GL_SCOPE();
FOpenGLTextureBase* Texture = GetOpenGLTextureFromRHITexture(SurfaceRHI);
if ( FOpenGL::SupportsGenerateMipmap())
{
GPUProfilingData.RegisterGPUWork(0);
FOpenGLContextState& ContextState = GetContextStateForCurrentContext();
// Setup the texture on a disused unit
// need to figure out how to setup mips properly in no views case
CachedSetupTextureStage(ContextState, FOpenGL::GetMaxCombinedTextureImageUnits() - 1, Texture->Target, Texture->Resource, -1, 1);
FOpenGL::GenerateMipmap( Texture->Target);
}
else
{
UE_LOG( LogRHI, Fatal, TEXT("Generate Mipmaps unsupported on this OpenGL version"));
}
}
/**
* Computes the size in memory required by a given texture.
*
* @param TextureRHI - Texture we want to know the size of
* @return - Size in Bytes
*/
uint32 FOpenGLDynamicRHI::RHIComputeMemorySize(FTextureRHIParamRef TextureRHI)
{
if(!TextureRHI)
{
return 0;
}
FOpenGLTextureBase* Texture = GetOpenGLTextureFromRHITexture(TextureRHI);
return Texture->GetMemorySize();
}
/**
* Starts an asynchronous texture reallocation. It may complete immediately if the reallocation
* could be performed without any reshuffling of texture memory, or if there isn't enough memory.
* The specified status counter will be decremented by 1 when the reallocation is complete (success or failure).
*
* Returns a new reference to the texture, which will represent the new mip count when the reallocation is complete.
* RHIGetAsyncReallocateTexture2DStatus() can be used to check the status of an ongoing or completed reallocation.
*
* @param Texture2D - Texture to reallocate
* @param NewMipCount - New number of mip-levels
* @param RequestStatus - Will be decremented by 1 when the reallocation is complete (success or failure).
* @return - New reference to the texture, or an invalid reference upon failure
*/
FTexture2DRHIRef FOpenGLDynamicRHI::RHIAsyncReallocateTexture2D(FTexture2DRHIParamRef Texture2DRHI, int32 NewMipCount, int32 NewSizeX, int32 NewSizeY, FThreadSafeCounter* RequestStatus)
{
VERIFY_GL_SCOPE();
FOpenGLTexture2D* Texture2D = ResourceCast(Texture2DRHI);
// Allocate a new texture.
FOpenGLTexture2D* NewTexture2D = (FOpenGLTexture2D*)CreateOpenGLTexture(NewSizeX,NewSizeY,false,false, Texture2D->GetFormat(),NewMipCount,1,1, Texture2D->GetFlags(), Texture2DRHI->GetClearBinding());
const uint32 BlockSizeX = GPixelFormats[Texture2D->GetFormat()].BlockSizeX;
const uint32 BlockSizeY = GPixelFormats[Texture2D->GetFormat()].BlockSizeY;
const uint32 NumBytesPerBlock = GPixelFormats[Texture2D->GetFormat()].BlockBytes;
// Should we use client-storage to improve update time on platforms that require it
const bool bCompressed = GOpenGLTextureFormats[Texture2D->GetFormat()].bCompressed;
bool const bRenderable = (Texture2D->GetFlags() & (TexCreate_RenderTargetable|TexCreate_ResolveTargetable|TexCreate_DepthStencilTargetable|TexCreate_CPUReadback)) != 0;
const bool bUseClientStorage = (FOpenGL::SupportsClientStorage() && !FOpenGL::SupportsTextureView() && !bRenderable && !bCompressed);
// Use the GPU to asynchronously copy the old mip-maps into the new texture.
const uint32 NumSharedMips = FMath::Min(Texture2D->GetNumMips(),NewTexture2D->GetNumMips());
const uint32 SourceMipOffset = Texture2D->GetNumMips() - NumSharedMips;
const uint32 DestMipOffset = NewTexture2D->GetNumMips() - NumSharedMips;
if (FOpenGL::SupportsCopyImage())
{
FOpenGLTexture2D *NewOGLTexture2D = (FOpenGLTexture2D*)NewTexture2D;
FOpenGLTexture2D *OGLTexture2D = (FOpenGLTexture2D*)Texture2D;
NewOGLTexture2D->CloneViaCopyImage( OGLTexture2D, NumSharedMips, SourceMipOffset, DestMipOffset);
}
else if (FOpenGL::SupportsCopyTextureLevels())
{
FOpenGL::CopyTextureLevels( NewTexture2D->Resource, Texture2D->Resource, SourceMipOffset, NumSharedMips);
}
else if (FOpenGL::SupportsPixelBuffers() && !bUseClientStorage)
{
FOpenGLTexture2D *NewOGLTexture2D = (FOpenGLTexture2D*)NewTexture2D;
FOpenGLTexture2D *OGLTexture2D = (FOpenGLTexture2D*)Texture2D;
NewOGLTexture2D->CloneViaPBO( OGLTexture2D, NumSharedMips, SourceMipOffset, DestMipOffset);
}
else
{
for(uint32 MipIndex = 0;MipIndex < NumSharedMips;++MipIndex)
{
const uint32 MipSizeX = FMath::Max<uint32>(1,NewSizeX >> (MipIndex+DestMipOffset));
const uint32 MipSizeY = FMath::Max<uint32>(1,NewSizeY >> (MipIndex+DestMipOffset));
const uint32 NumBlocksX = AlignArbitrary(MipSizeX, BlockSizeX) / BlockSizeX;
const uint32 NumBlocksY = AlignArbitrary(MipSizeY, BlockSizeY) / BlockSizeY;
const uint32 NumMipBlocks = NumBlocksX * NumBlocksY;
// Lock old and new texture.
uint32 SrcStride;
uint32 DestStride;
void* Src = RHILockTexture2D( Texture2D, MipIndex + SourceMipOffset, RLM_ReadOnly, SrcStride, false );
void* Dst = RHILockTexture2D( NewTexture2D, MipIndex + DestMipOffset, RLM_WriteOnly, DestStride, false );
check(SrcStride == DestStride);
FMemory::Memcpy( Dst, Src, NumMipBlocks * NumBytesPerBlock );
RHIUnlockTexture2D( Texture2D, MipIndex + SourceMipOffset, false );
RHIUnlockTexture2D( NewTexture2D, MipIndex + DestMipOffset, false );
}
}
// Decrement the thread-safe counter used to track the completion of the reallocation, since D3D handles sequencing the
// async mip copies with other D3D calls.
RequestStatus->Decrement();
return NewTexture2D;
}
/**
* Returns the status of an ongoing or completed texture reallocation:
* TexRealloc_Succeeded - The texture is ok, reallocation is not in progress.
* TexRealloc_Failed - The texture is bad, reallocation is not in progress.
* TexRealloc_InProgress - The texture is currently being reallocated async.
*
* @param Texture2D - Texture to check the reallocation status for
* @return - Current reallocation status
*/
ETextureReallocationStatus FOpenGLDynamicRHI::RHIFinalizeAsyncReallocateTexture2D( FTexture2DRHIParamRef Texture2D, bool bBlockUntilCompleted )
{
return TexRealloc_Succeeded;
}
/**
* Cancels an async reallocation for the specified texture.
* This should be called for the new texture, not the original.
*
* @param Texture Texture to cancel
* @param bBlockUntilCompleted If true, blocks until the cancellation is fully completed
* @return Reallocation status
*/
ETextureReallocationStatus FOpenGLDynamicRHI::RHICancelAsyncReallocateTexture2D( FTexture2DRHIParamRef Texture2D, bool bBlockUntilCompleted )
{
return TexRealloc_Succeeded;
}
void* FOpenGLDynamicRHI::RHILockTexture2D(FTexture2DRHIParamRef TextureRHI,uint32 MipIndex,EResourceLockMode LockMode,uint32& DestStride,bool bLockWithinMiptail)
{
FOpenGLTexture2D* Texture = ResourceCast(TextureRHI);
return Texture->Lock(MipIndex,0,LockMode,DestStride);
}
void FOpenGLDynamicRHI::RHIUnlockTexture2D(FTexture2DRHIParamRef TextureRHI,uint32 MipIndex,bool bLockWithinMiptail)
{
FOpenGLTexture2D* Texture = ResourceCast(TextureRHI);
Texture->Unlock(MipIndex, 0);
}
void* FOpenGLDynamicRHI::RHILockTexture2DArray(FTexture2DArrayRHIParamRef TextureRHI,uint32 TextureIndex,uint32 MipIndex,EResourceLockMode LockMode,uint32& DestStride,bool bLockWithinMiptail)
{
FOpenGLTexture2DArray* Texture = ResourceCast(TextureRHI);
return Texture->Lock(MipIndex,TextureIndex,LockMode,DestStride);
}
void FOpenGLDynamicRHI::RHIUnlockTexture2DArray(FTexture2DArrayRHIParamRef TextureRHI,uint32 TextureIndex,uint32 MipIndex,bool bLockWithinMiptail)
{
FOpenGLTexture2DArray* Texture = ResourceCast(TextureRHI);
Texture->Unlock(MipIndex, TextureIndex);
}
void FOpenGLDynamicRHI::RHIUpdateTexture2D(FTexture2DRHIParamRef TextureRHI,uint32 MipIndex,const FUpdateTextureRegion2D& UpdateRegion,uint32 SourcePitch,const uint8* SourceData)
{
VERIFY_GL_SCOPE();
FOpenGLTexture2D* Texture = ResourceCast(TextureRHI);
// Use a texture stage that's not likely to be used for draws, to avoid waiting
FOpenGLContextState& ContextState = GetContextStateForCurrentContext();
CachedSetupTextureStage(ContextState, FOpenGL::GetMaxCombinedTextureImageUnits() - 1, Texture->Target, Texture->Resource, 0, Texture->GetNumMips());
CachedBindPixelUnpackBuffer(ContextState, 0);
EPixelFormat PixelFormat = Texture->GetFormat();
check(GPixelFormats[PixelFormat].BlockSizeX == 1);
check(GPixelFormats[PixelFormat].BlockSizeY == 1);
const FOpenGLTextureFormat& GLFormat = GOpenGLTextureFormats[PixelFormat];
const uint32 FormatBPP = GPixelFormats[PixelFormat].BlockBytes;
checkf(!GLFormat.bCompressed, TEXT("RHIUpdateTexture2D not currently supported for compressed (%s) textures by the OpenGL RHI"), GPixelFormats[PixelFormat].Name);
glPixelStorei(GL_UNPACK_ROW_LENGTH, SourcePitch / FormatBPP);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexSubImage2D(Texture->Target, MipIndex, UpdateRegion.DestX, UpdateRegion.DestY, UpdateRegion.Width, UpdateRegion.Height,
GLFormat.Format, GLFormat.Type, SourceData);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
// No need to restore texture stage; leave it like this,
// and the next draw will take care of cleaning it up; or
// next operation that needs the stage will switch something else in on it.
}
void FOpenGLDynamicRHI::RHIUpdateTexture3D(FTexture3DRHIParamRef TextureRHI,uint32 MipIndex,const FUpdateTextureRegion3D& UpdateRegion,uint32 SourceRowPitch,uint32 SourceDepthPitch,const uint8* SourceData)
{
VERIFY_GL_SCOPE();
check( FOpenGL::SupportsTexture3D() );
FOpenGLTexture3D* Texture = ResourceCast(TextureRHI);
// Use a texture stage that's not likely to be used for draws, to avoid waiting
FOpenGLContextState& ContextState = GetContextStateForCurrentContext();
CachedSetupTextureStage(ContextState, FOpenGL::GetMaxCombinedTextureImageUnits() - 1, Texture->Target, Texture->Resource, 0, Texture->GetNumMips());
CachedBindPixelUnpackBuffer(ContextState, 0);
EPixelFormat PixelFormat = Texture->GetFormat();
check(GPixelFormats[PixelFormat].BlockSizeX == 1);
check(GPixelFormats[PixelFormat].BlockSizeY == 1);
// TO DO - add appropriate offsets to source data when necessary
check(UpdateRegion.SrcX == 0);
check(UpdateRegion.SrcY == 0);
check(UpdateRegion.SrcZ == 0);
const FOpenGLTextureFormat& GLFormat = GOpenGLTextureFormats[PixelFormat];
const uint32 FormatBPP = GPixelFormats[PixelFormat].BlockBytes;
checkf(!GLFormat.bCompressed, TEXT("RHIUpdateTexture3D not currently supported for compressed (%s) textures by the OpenGL RHI"), GPixelFormats[PixelFormat].Name);
glPixelStorei(GL_UNPACK_ROW_LENGTH, SourceRowPitch / FormatBPP);
check( SourceDepthPitch % ( FormatBPP * UpdateRegion.Width ) == 0 );
glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, SourceDepthPitch / UpdateRegion.Width / FormatBPP);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
FOpenGL::TexSubImage3D(Texture->Target, MipIndex, UpdateRegion.DestX, UpdateRegion.DestY, UpdateRegion.DestZ, UpdateRegion.Width, UpdateRegion.Height, UpdateRegion.Depth,
GLFormat.Format, GLFormat.Type, SourceData);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
// No need to restore texture stage; leave it like this,
// and the next draw will take care of cleaning it up; or
// next operation that needs the stage will switch something else in on it.
}
void FOpenGLDynamicRHI::InvalidateTextureResourceInCache(GLuint Resource)
{
for (int32 SamplerIndex = 0; SamplerIndex < FOpenGL::GetMaxCombinedTextureImageUnits(); ++SamplerIndex)
{
if (SharedContextState.Textures[SamplerIndex].Resource == Resource)
{
SharedContextState.Textures[SamplerIndex].Target = GL_NONE;
SharedContextState.Textures[SamplerIndex].Resource = 0;
}
if (RenderingContextState.Textures[SamplerIndex].Resource == Resource)
{
RenderingContextState.Textures[SamplerIndex].Target = GL_NONE;
RenderingContextState.Textures[SamplerIndex].Resource = 0;
}
}
TextureMipLimits.Remove(Resource);
if (PendingState.DepthStencil && PendingState.DepthStencil->Resource == Resource)
{
PendingState.DepthStencil = nullptr;
}
}
void FOpenGLDynamicRHI::InvalidateUAVResourceInCache(GLuint Resource)
{
for (int32 UAVIndex = 0; UAVIndex < OGL_MAX_COMPUTE_STAGE_UAV_UNITS; ++UAVIndex)
{
if (SharedContextState.UAVs[UAVIndex].Resource == Resource)
{
SharedContextState.UAVs[UAVIndex].Format = GL_NONE;
SharedContextState.UAVs[UAVIndex].Resource = 0;
}
if (RenderingContextState.UAVs[UAVIndex].Resource == Resource)
{
RenderingContextState.UAVs[UAVIndex].Format = GL_NONE;
RenderingContextState.UAVs[UAVIndex].Resource = 0;
}
}
}
/*-----------------------------------------------------------------------------
Cubemap texture support.
-----------------------------------------------------------------------------*/
FTextureCubeRHIRef FOpenGLDynamicRHI::RHICreateTextureCube( uint32 Size, uint8 Format, uint32 NumMips, uint32 Flags, FRHIResourceCreateInfo& CreateInfo )
{
// not yet supported
check(!CreateInfo.BulkData);
return (FRHITextureCube*)CreateOpenGLTexture(Size,Size,true, false, Format, NumMips, 1, 1, Flags, CreateInfo.ClearValueBinding);
}
FTextureCubeRHIRef FOpenGLDynamicRHI::RHICreateTextureCubeArray( uint32 Size, uint32 ArraySize, uint8 Format, uint32 NumMips, uint32 Flags, FRHIResourceCreateInfo& CreateInfo )
{
// not yet supported
check(!CreateInfo.BulkData);
return (FRHITextureCube*)CreateOpenGLTexture(Size, Size, true, true, Format, NumMips, 1, 6 * ArraySize, Flags, CreateInfo.ClearValueBinding);
}
void* FOpenGLDynamicRHI::RHILockTextureCubeFace(FTextureCubeRHIParamRef TextureCubeRHI,uint32 FaceIndex,uint32 ArrayIndex,uint32 MipIndex,EResourceLockMode LockMode,uint32& DestStride,bool bLockWithinMiptail)
{
FOpenGLTextureCube* TextureCube = ResourceCast(TextureCubeRHI);
return TextureCube->Lock(MipIndex,FaceIndex + 6 * ArrayIndex,LockMode,DestStride);
}
void FOpenGLDynamicRHI::RHIUnlockTextureCubeFace(FTextureCubeRHIParamRef TextureCubeRHI,uint32 FaceIndex,uint32 ArrayIndex,uint32 MipIndex,bool bLockWithinMiptail)
{
FOpenGLTextureCube* TextureCube = ResourceCast(TextureCubeRHI);
TextureCube->Unlock(MipIndex,FaceIndex + ArrayIndex * 6);
}
void FOpenGLDynamicRHI::RHIBindDebugLabelName(FTextureRHIParamRef TextureRHI, const TCHAR* Name)
{
FOpenGLTextureBase* Texture = GetOpenGLTextureFromRHITexture(TextureRHI);
FOpenGL::LabelObject(GL_TEXTURE, Texture->Resource, TCHAR_TO_ANSI(Name));
}
void FOpenGLDynamicRHI::RHIVirtualTextureSetFirstMipInMemory(FTexture2DRHIParamRef TextureRHI, uint32 FirstMip)
{
}
void FOpenGLDynamicRHI::RHIVirtualTextureSetFirstMipVisible(FTexture2DRHIParamRef TextureRHI, uint32 FirstMip)
{
}
FTextureReferenceRHIRef FOpenGLDynamicRHI::RHICreateTextureReference(FLastRenderTimeContainer* InLastRenderTime)
{
return new FOpenGLTextureReference(InLastRenderTime);
}
void FOpenGLTextureReference::SetReferencedTexture(FRHITexture* InTexture)
{
FRHITextureReference::SetReferencedTexture(InTexture);
TexturePtr = GetOpenGLTextureFromRHITexture(InTexture);
}
void FOpenGLDynamicRHI::RHIUpdateTextureReference(FTextureReferenceRHIParamRef TextureRefRHI, FTextureRHIParamRef NewTextureRHI)
{
auto* TextureRef = (FOpenGLTextureReference*)TextureRefRHI;
if (TextureRef)
{
TextureRef->SetReferencedTexture(NewTextureRHI);
}
}