// Copyright Epic Games, Inc. All Rights Reserved. #include "CoreMinimal.h" #include "RenderUtils.h" #include "OpenGLDrv.h" #include "OpenGLDrvPrivate.h" #include "ClearReplacementShaders.h" void FOpenGLViewableResource::UpdateLinkedViews() { for (FOpenGLView* View = LinkedViews; View; View = View->Next()) { View->UpdateView(); } } // Binds the specified buffer range to a texture resource and selects glTexBuffer or glTexBufferRange static void BindGLTexBufferRange(GLenum Target, GLenum InternalFormat, GLuint Buffer, uint32 StartOffsetBytes, uint32 NumElements, uint32 Stride) { if (StartOffsetBytes == 0 && NumElements == UINT32_MAX) { FOpenGL::TexBuffer(Target, InternalFormat, Buffer); } else { // Validate buffer offset is a multiple of buffer offset alignment GLintptr Offset = StartOffsetBytes; GLsizeiptr Size = NumElements * Stride; #if DO_CHECK GLint Alignment = FOpenGLBase::GetTextureBufferAlignment(); check(Stride > 0 && Offset % Alignment == 0); #endif FOpenGL::TexBufferRange(Target, InternalFormat, Buffer, Offset, Size); } } FOpenGLShaderResourceView::FOpenGLShaderResourceView(FRHICommandListBase& RHICmdList, FRHIViewableResource* InResource, FRHIViewDesc const& InViewDesc) : FRHIShaderResourceView(InResource, InViewDesc) { RHICmdList.EnqueueLambda([this](FRHICommandListBase&) { LinkHead(GetBaseResource()->LinkedViews); UpdateView(); }); } FOpenGLViewableResource* FOpenGLShaderResourceView::GetBaseResource() const { return IsBuffer() ? static_cast(FOpenGLDynamicRHI::ResourceCast(GetBuffer())) : static_cast(FOpenGLDynamicRHI::ResourceCast(GetTexture())); } void FOpenGLShaderResourceView::UpdateView() { VERIFY_GL_SCOPE(); Invalidate(); if (ViewDesc.IsBuffer()) { FOpenGLBuffer* Buffer = FOpenGLDynamicRHI::ResourceCast(GetBuffer()); auto const Info = ViewDesc.Buffer.SRV.GetViewInfo(Buffer); if (!Info.bNullView) { switch (Info.BufferType) { case FRHIViewDesc::EBufferType::Typed: Target = GL_TEXTURE_BUFFER; OwnsResource = true; FOpenGL::GenTextures(1, &Resource); // Use a texture stage that's not likely to be used for draws, to avoid waiting FOpenGLDynamicRHI::Get().CachedSetupTextureStage( FOpenGL::GetMaxCombinedTextureImageUnits() - 1, GL_TEXTURE_BUFFER, Resource, -1, 1 ); BindGLTexBufferRange( GL_TEXTURE_BUFFER, GOpenGLTextureFormats[Info.Format].InternalFormat[0], Buffer->Resource, Info.OffsetInBytes, Info.NumElements, GPixelFormats[Info.Format].BlockBytes ); break; case FRHIViewDesc::EBufferType::Raw: case FRHIViewDesc::EBufferType::Structured: //TODO: add range views for SSBO ensure(Info.OffsetInBytes == 0 && Info.SizeInBytes == Buffer->GetDesc().Size); Target = GL_SHADER_STORAGE_BUFFER; Resource = Buffer->Resource; break; default: checkNoEntry(); // not implemented break; } } } else { FOpenGLTexture* Texture = FOpenGLDynamicRHI::ResourceCast(GetTexture()); auto const Info = ViewDesc.Texture.SRV.GetViewInfo(Texture); Target = Texture->Target; LimitMip = Info.MipRange.Num == 1 ? Info.MipRange.First : -1; ensureMsgf(Info.MipRange.ExclusiveLast() == Texture->GetDesc().NumMips, TEXT("OpenGL RHI does not currently implement creation of SRVs that don't include all mips in the mip tail.")); ensureMsgf(Info.bAllSlices, TEXT("SRV array slice selection is currently unimplemented in OpenGL RHI.")); const bool bFormatsMatch = Info.Format == Texture->GetFormat() || Info.Format == PF_X24_G8; checkf(bFormatsMatch, TEXT("SRVs cannot modify the pixel format of a texture when texture views are unsupported.")); Resource = Texture->GetResource(); // Handle the custom stencil SRV if (Info.Format == PF_X24_G8) { // Use a texture stage that's not likely to be used for draws, to avoid waiting FOpenGLDynamicRHI::Get().CachedSetupTextureStage(FOpenGL::GetMaxCombinedTextureImageUnits() - 1, Target, Resource, LimitMip, Texture->GetNumMips()); //set the texture to return the stencil index, and then force the components to match D3D glTexParameteri(Target, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX); 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); } } } void FOpenGLShaderResourceView::Invalidate() { if (Resource && OwnsResource) { FOpenGLDynamicRHI::Get().InvalidateTextureResourceInCache(Resource); FOpenGL::DeleteTextures(1, &Resource); } Resource = GL_NONE; Target = GL_TEXTURE_BUFFER; LimitMip = -1; OwnsResource = false; } FOpenGLShaderResourceView::~FOpenGLShaderResourceView() { VERIFY_GL_SCOPE(); Unlink(); Invalidate(); } FOpenGLUnorderedAccessView::FOpenGLUnorderedAccessView(FRHICommandListBase& RHICmdList, FRHIViewableResource* InResource, FRHIViewDesc const& InViewDesc) : FRHIUnorderedAccessView(InResource, InViewDesc) { RHICmdList.EnqueueLambda([this](FRHICommandListBase&) { LinkHead(GetBaseResource()->LinkedViews); UpdateView(); }); } FOpenGLViewableResource* FOpenGLUnorderedAccessView::GetBaseResource() const { return IsBuffer() ? static_cast(FOpenGLDynamicRHI::ResourceCast(GetBuffer())) : static_cast(FOpenGLDynamicRHI::ResourceCast(GetTexture())); } void FOpenGLUnorderedAccessView::UpdateView() { VERIFY_GL_SCOPE(); Invalidate(); if (IsBuffer()) { FOpenGLBuffer* Buffer = FOpenGLDynamicRHI::ResourceCast(GetBuffer()); auto const Info = ViewDesc.Buffer.UAV.GetViewInfo(Buffer); if (!Info.bNullView) { const FOpenGLTextureFormat& GLFormat = GOpenGLTextureFormats[Info.Format]; this->UnrealFormat = Info.Format; this->Format = GLFormat.InternalFormat[0]; ensureMsgf(Info.SizeInBytes == Buffer->GetDesc().Size && Info.OffsetInBytes == 0, TEXT("Only whole-resource UAVs are currently implemented in OpenGL RHI")); checkf(!Info.bAppendBuffer && !Info.bAtomicCounter, TEXT("Atomic counter / append buffer not implemented in OpenGL RHI.")); switch (Info.BufferType) { case FRHIViewDesc::EBufferType::Typed: { GLuint TextureID = 0; FOpenGL::GenTextures(1, &TextureID); // Use a texture stage that's not likely to be used for draws, to avoid waiting FOpenGLDynamicRHI::Get().CachedSetupTextureStage(FOpenGL::GetMaxCombinedTextureImageUnits() - 1, GL_TEXTURE_BUFFER, TextureID, -1, 1); FOpenGL::TexBuffer(GL_TEXTURE_BUFFER, GLFormat.InternalFormat[0], Buffer->Resource); // 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. this->Resource = TextureID; this->OwnsResource = true; } break; case FRHIViewDesc::EBufferType::Structured: case FRHIViewDesc::EBufferType::Raw: this->BufferResource = Buffer->Resource; break; default: checkNoEntry(); break; } } } else { FOpenGLTexture* Texture = FOpenGLDynamicRHI::ResourceCast(GetTexture()); auto const Info = ViewDesc.Texture.UAV.GetViewInfo(Texture); ensureMsgf(Info.bAllSlices, TEXT("UAV array slice selection is currently unimplemented in OpenGL RHI.")); const FOpenGLTextureFormat& GLFormat = GOpenGLTextureFormats[Info.Format]; check(!Texture->CanBeEvicted() && !Texture->IsEvicted()); this->Resource = Texture->GetResource(); this->Format = GLFormat.InternalFormat[0]; this->UnrealFormat = Info.Format; this->Level = Info.MipLevel; } } void FOpenGLUnorderedAccessView::Invalidate() { if (Resource && OwnsResource) { FOpenGLDynamicRHI::Get().InvalidateTextureResourceInCache(Resource); FOpenGL::DeleteTextures(1, &Resource); } Resource = 0; BufferResource = 0; Format = 0; UnrealFormat = 0; Level = 0; } FOpenGLUnorderedAccessView::~FOpenGLUnorderedAccessView() { VERIFY_GL_SCOPE(); Unlink(); Invalidate(); } void FOpenGLDynamicRHI::RHIClearUAVFloat(FRHIUnorderedAccessView* UnorderedAccessViewRHI, const FVector4f& Values) { FOpenGLUnorderedAccessView* UAV = ResourceCast(UnorderedAccessViewRHI); // Use compute on ES3.1 TRHICommandList_RecursiveHazardous RHICmdList(this); if (UAV->IsTexture()) { FIntVector Size = UAV->GetTexture()->GetSizeXYZ(); if (UAV->IsLayered()) { ClearUAVShader_T(RHICmdList, UnorderedAccessViewRHI, Size.X, Size.Y, Size.Z, *reinterpret_cast(&Values)); } else { ClearUAVShader_T(RHICmdList, UnorderedAccessViewRHI, Size.X, Size.Y, Size.Z, *reinterpret_cast(&Values)); } } else { if (UAV->UnrealFormat != 0) { // Typed buffer ClearUAVShader_T( RHICmdList , UnorderedAccessViewRHI , UAV->GetBufferSize() / GPixelFormats[UAV->UnrealFormat].BlockBytes , 1 , 1 , *reinterpret_cast(&Values) ); } else { // Storage buffer ClearUAVShader_T( RHICmdList , UnorderedAccessViewRHI , UAV->GetBufferSize() / sizeof(float) , 1 , 1 , *reinterpret_cast(&Values) ); } } } void FOpenGLDynamicRHI::RHIClearUAVUint(FRHIUnorderedAccessView* UnorderedAccessViewRHI, const FUintVector4& Values) { FOpenGLUnorderedAccessView* UAV = ResourceCast(UnorderedAccessViewRHI); TRHICommandList_RecursiveHazardous RHICmdList(this); if (UAV->IsTexture()) { FIntVector Size = UAV->GetTexture()->GetSizeXYZ(); if (UAV->IsLayered()) { ClearUAVShader_T(RHICmdList, UnorderedAccessViewRHI, Size.X, Size.Y, Size.Z, *reinterpret_cast(&Values)); } else { ClearUAVShader_T(RHICmdList, UnorderedAccessViewRHI, Size.X, Size.Y, Size.Z, *reinterpret_cast(&Values)); } } else { if (UAV->UnrealFormat != 0) { // Typed buffer ClearUAVShader_T( RHICmdList , UnorderedAccessViewRHI , UAV->GetBufferSize() / GPixelFormats[UAV->UnrealFormat].BlockBytes , 1 , 1 , *reinterpret_cast(&Values) ); } else { // Storage buffer ClearUAVShader_T( RHICmdList , UnorderedAccessViewRHI , UAV->GetBufferSize() / sizeof(uint32) , 1 , 1 , *reinterpret_cast(&Values) ); } } } FShaderResourceViewRHIRef FOpenGLDynamicRHI::RHICreateShaderResourceView(class FRHICommandListBase& RHICmdList, FRHIViewableResource* Resource, FRHIViewDesc const& ViewDesc) { return new FOpenGLShaderResourceView(RHICmdList, Resource, ViewDesc); } FUnorderedAccessViewRHIRef FOpenGLDynamicRHI::RHICreateUnorderedAccessView(class FRHICommandListBase& RHICmdList, FRHIViewableResource* Resource, FRHIViewDesc const& ViewDesc) { return new FOpenGLUnorderedAccessView(RHICmdList, Resource, ViewDesc); }