Files
UnrealEngineUWP/Engine/Source/Runtime/OpenGLDrv/Private/OpenGLUAV.cpp
zach bethel f457a69101 Added RHI tracked access API to remove Unknown transitions.
- New RHI command list SetTrackedAccess method for the user to supply a current whole-resource state.
 - New RHI command context GetTrackedAccess method for querying the tracked access in RHIBeginTransitions / RHIEndTransitions on the RHI thread.
 - Hooked RHICmdList.Transition and FRHICommandListExecutor::Transition to assign tracked state automatically.
 - Refactored RDG and resource pools to use new RHI tracking.
      - FRDGPooledBuffer / FRDGPooledTexture no longer contain tracked state. RDG temp-allocates state through the graph allocator instead.
      - All prologue transitions are 'Unknown', and all epilogue transitions coalesce into a whole resource state.
 - Implemented platform support for patching the 'before' state with the tracked state.
 - Implemented various RHI validation checks:
      - Asserts that the user assigned tracked state matches RHI validation tracked state, for all subresources.
      - Asserts that tracked state is not assigned or queried from a parallel translation context.
 - Added FRHIViewableResource and FRHIView base classes to RHI. FRHIView contains a pointer to an FRHIViewableResource. This is currently a raw pointer, but should be extended to a full reference in a later CL.

NOTE on RHI thread constraint:

Transition evaluation is now restricted to the RHI thread (i.e. no parallel translation contexts). Transitions aren't performed in parallel translate contexts anyway, so this is not a problem. If, however, we decide to refactor parallel translation to be more general, this implementation could be extended to track the state per context and update from the 'dispatch' thread.

#preflight 6233b4396666d7e753a16aaf
#rb kenzo.terelst

[CL 19513316 by zach bethel in ue5-main branch]
2022-03-25 11:19:10 -04:00

394 lines
13 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "CoreMinimal.h"
#include "RenderUtils.h"
#include "OpenGLDrv.h"
#include "OpenGLDrvPrivate.h"
#include "ClearReplacementShaders.h"
// 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(const FShaderResourceViewInitializer& Initializer)
: FRHIShaderResourceView(Initializer.AsBufferSRV().Buffer)
{
FShaderResourceViewInitializer::FBufferShaderResourceViewInitializer Desc = Initializer.AsBufferSRV();
Buffer = FOpenGLDynamicRHI::ResourceCast(Desc.Buffer);
if (Buffer == nullptr)
{
return;
}
EPixelFormat Format = Desc.Format;
if (Initializer.GetType() == FShaderResourceViewInitializer::EType::IndexBufferSRV && Format == PF_Unknown)
{
uint32 Stride = Buffer->GetStride();
Format = (Stride == 2) ? PF_R16_UINT : PF_R32_UINT;
}
if (Format != PF_Unknown)
{
Target = GL_TEXTURE_BUFFER;
OwnsResource = true;
UE_CLOG(!GPixelFormats[Format].Supported, LogRHI, Error, TEXT("Unsupported EPixelFormat %d"), Format);
RunOnGLRenderContextThread([this, Format, StartOffsetBytes = Desc.StartOffsetBytes, NumElements = Desc.NumElements]()
{
FOpenGL::GenTextures(1, &Resource);
// Use a texture stage that's not likely to be used for draws, to avoid waiting
FOpenGLDynamicRHI::Get().CachedSetupTextureStage(
FOpenGLDynamicRHI::Get().GetContextStateForCurrentContext(),
FOpenGL::GetMaxCombinedTextureImageUnits() - 1,
GL_TEXTURE_BUFFER,
Resource,
-1,
1
);
BindGLTexBufferRange(
GL_TEXTURE_BUFFER,
GOpenGLTextureFormats[Format].InternalFormat[0],
Buffer->Resource,
StartOffsetBytes,
NumElements,
GPixelFormats[Format].BlockBytes
);
});
}
else
{
//TODO: add range views for SSBO
ensure(Desc.IsWholeResource());
Target = GL_SHADER_STORAGE_BUFFER;
RunOnGLRenderContextThread([this]()
{
Resource = Buffer->Resource;
});
}
}
FShaderResourceViewRHIRef FOpenGLDynamicRHI::RHICreateShaderResourceView(const FShaderResourceViewInitializer& Initializer)
{
return new FOpenGLShaderResourceView(Initializer);
}
FShaderResourceViewRHIRef FOpenGLDynamicRHI::RHICreateShaderResourceView(FRHIBuffer* BufferRHI, uint32 Stride, uint8 Format)
{
ensureMsgf(Stride == GPixelFormats[Format].BlockBytes, TEXT("provided stride: %i was not consitent with Pixelformat: %s"), Stride, GPixelFormats[Format].Name);
return FOpenGLDynamicRHI::RHICreateShaderResourceView(FShaderResourceViewInitializer(BufferRHI, EPixelFormat(Format)));
}
FShaderResourceViewRHIRef FOpenGLDynamicRHI::RHICreateShaderResourceView(FRHIBuffer* BufferRHI)
{
return FOpenGLDynamicRHI::RHICreateShaderResourceView(FShaderResourceViewInitializer(BufferRHI));
}
void FOpenGLDynamicRHI::RHIUpdateShaderResourceView(FRHIShaderResourceView* SRV, FRHIBuffer* InBuffer)
{
VERIFY_GL_SCOPE();
FOpenGLShaderResourceView* SRVGL = FOpenGLDynamicRHI::ResourceCast(SRV);
FOpenGLBuffer* BufferGL = FOpenGLDynamicRHI::ResourceCast(InBuffer);
check(SRVGL);
if (SRVGL->Target == GL_TEXTURE_BUFFER)
{
GLuint TextureID = SRVGL->Resource;
CachedSetupTextureStage(GetContextStateForCurrentContext(), FOpenGL::GetMaxCombinedTextureImageUnits() - 1, GL_TEXTURE_BUFFER, TextureID, -1, 1);
if (!BufferGL)
{
FOpenGL::TexBuffer(GL_TEXTURE_BUFFER, GL_R16UI, 0); // format ignored here since we're detaching.
SRVGL->Buffer = nullptr;
}
else
{
uint32 Stride = BufferGL->GetStride();
GLenum Format = (Stride == 2) ? GL_R16UI : GL_R32UI;
uint32 NumElements = BufferGL->GetSize() / Stride;
BindGLTexBufferRange(GL_TEXTURE_BUFFER, Format, BufferGL->Resource, 0, NumElements, Stride);
SRVGL->Buffer = BufferGL;
}
}
else if (SRVGL->Target == GL_SHADER_STORAGE_BUFFER)
{
SRVGL->Resource = BufferGL->Resource;
SRVGL->Buffer = BufferGL;
}
else
{
checkNoEntry();
}
}
void FOpenGLDynamicRHI::RHIUpdateShaderResourceView(FRHIShaderResourceView* SRV, FRHIBuffer* InBuffer, uint32 Stride, uint8 Format)
{
VERIFY_GL_SCOPE();
FOpenGLShaderResourceView* SRVGL = FOpenGLDynamicRHI::ResourceCast(SRV);
FOpenGLBuffer* BufferGL = FOpenGLDynamicRHI::ResourceCast(InBuffer);
const FOpenGLTextureFormat& GLFormat = GOpenGLTextureFormats[Format];
check(SRVGL);
check(SRVGL->Target == GL_TEXTURE_BUFFER); // add support for SSBO views?
GLuint TextureID = SRVGL->Resource;
CachedSetupTextureStage(GetContextStateForCurrentContext(), FOpenGL::GetMaxCombinedTextureImageUnits() - 1, GL_TEXTURE_BUFFER, TextureID, -1, 1);
if (!BufferGL)
{
FOpenGL::TexBuffer(GL_TEXTURE_BUFFER, GLFormat.InternalFormat[0], 0);
SRVGL->Buffer = nullptr;
}
else
{
FOpenGL::TexBuffer(GL_TEXTURE_BUFFER, GLFormat.InternalFormat[0], BufferGL->Resource);
SRVGL->Buffer = BufferGL;
}
}
FOpenGLShaderResourceView::~FOpenGLShaderResourceView()
{
if (Resource && OwnsResource)
{
RunOnGLRenderContextThread([Resource = Resource]()
{
VERIFY_GL_SCOPE();
FOpenGLDynamicRHI::Get().InvalidateTextureResourceInCache(Resource);
FOpenGL::DeleteTextures(1, &Resource);
});
}
}
FUnorderedAccessViewRHIRef FOpenGLDynamicRHI::RHICreateUnorderedAccessView(FRHITexture* TextureRHI, uint32 MipLevel, uint16 FirstArraySlice, uint16 NumArraySlices)
{
check(TextureRHI->GetFlags() & TexCreate_UAV);
// Slice selection of a texture array still need to be implemented on OpenGL
check(FirstArraySlice == 0 && NumArraySlices == 0);
return new FOpenGLTextureUnorderedAccessView(TextureRHI);
}
FOpenGLTextureUnorderedAccessView::FOpenGLTextureUnorderedAccessView(FRHITexture* InTextureRHI):
FOpenGLUnorderedAccessView(InTextureRHI),
TextureRHI(InTextureRHI)
{
VERIFY_GL_SCOPE();
FOpenGLTexture* Texture = GetOpenGLTextureFromRHITexture(TextureRHI);
const FOpenGLTextureFormat& GLFormat = GOpenGLTextureFormats[TextureRHI->GetFormat()];
check(!Texture->CanBeEvicted() && !Texture->IsEvicted());
this->Resource = Texture->GetResource();
this->Format = GLFormat.InternalFormat[0];
this->UnrealFormat = TextureRHI->GetFormat();
this->bLayered = (Texture->Target == GL_TEXTURE_3D);
}
FUnorderedAccessViewRHIRef FOpenGLDynamicRHI::RHICreateUnorderedAccessView(FRHIBuffer* BufferRHI, uint8 Format)
{
check(BufferRHI->GetUsage() & BUF_UnorderedAccess);
return new FOpenGLTexBufferUnorderedAccessView(this, BufferRHI, Format);
}
FOpenGLTexBufferUnorderedAccessView::FOpenGLTexBufferUnorderedAccessView(FOpenGLDynamicRHI* InOpenGLRHI, FRHIBuffer* InBufferRHI, uint8 Format):
FOpenGLUnorderedAccessView(InBufferRHI),
BufferRHI(InBufferRHI),
OpenGLRHI(InOpenGLRHI)
{
VERIFY_GL_SCOPE();
FOpenGLBuffer* InBuffer = FOpenGLDynamicRHI::ResourceCast(InBufferRHI);
const FOpenGLTextureFormat& GLFormat = GOpenGLTextureFormats[Format];
GLuint TextureID = 0;
FOpenGL::GenTextures(1, &TextureID);
// Use a texture stage that's not likely to be used for draws, to avoid waiting
OpenGLRHI->CachedSetupTextureStage(OpenGLRHI->GetContextStateForCurrentContext(), FOpenGL::GetMaxCombinedTextureImageUnits() - 1, GL_TEXTURE_BUFFER, TextureID, -1, 1);
FOpenGL::TexBuffer(GL_TEXTURE_BUFFER, GLFormat.InternalFormat[0], InBuffer->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->BufferResource = InBuffer->Resource;
this->Format = GLFormat.InternalFormat[0];
this->UnrealFormat = Format;
}
uint32 FOpenGLTexBufferUnorderedAccessView::GetBufferSize()
{
return BufferRHI->GetSize();
}
FOpenGLTexBufferUnorderedAccessView::~FOpenGLTexBufferUnorderedAccessView()
{
if (Resource)
{
RunOnGLRenderContextThread([OpenGLRHI= OpenGLRHI, Resource = Resource]()
{
VERIFY_GL_SCOPE();
OpenGLRHI->InvalidateTextureResourceInCache(Resource);
FOpenGL::DeleteTextures(1, &Resource);
});
}
}
FUnorderedAccessViewRHIRef FOpenGLDynamicRHI::RHICreateUnorderedAccessView(FRHIBuffer* BufferRHI, bool bUseUAVCounter, bool bAppendBuffer)
{
check(BufferRHI->GetUsage() & BUF_UnorderedAccess);
return new FOpenGLBufferUnorderedAccessView(this, BufferRHI);
}
FOpenGLBufferUnorderedAccessView::FOpenGLBufferUnorderedAccessView(FOpenGLDynamicRHI* InOpenGLRHI, FRHIBuffer* InBufferRHI)
: FOpenGLUnorderedAccessView(InBufferRHI)
, BufferRHI(InBufferRHI)
, OpenGLRHI(InOpenGLRHI)
{
VERIFY_GL_SCOPE();
FOpenGLBuffer* Buffer = FOpenGLDynamicRHI::ResourceCast(BufferRHI.GetReference());
this->BufferResource = Buffer->Resource;
}
uint32 FOpenGLBufferUnorderedAccessView::GetBufferSize()
{
return BufferRHI->GetSize();
}
FOpenGLBufferUnorderedAccessView::~FOpenGLBufferUnorderedAccessView()
{
// not clearing cached state as SSBO can still be bound as SRV
}
void FOpenGLDynamicRHI::RHIClearUAVFloat(FRHIUnorderedAccessView* UnorderedAccessViewRHI, const FVector4f& Values)
{
FOpenGLUnorderedAccessView* Texture = ResourceCast(UnorderedAccessViewRHI);
// Use compute on ES3.1
TRHICommandList_RecursiveHazardous<FOpenGLDynamicRHI> RHICmdList(this);
if (Texture->GetBufferSize() == 0)
{
FOpenGLTextureUnorderedAccessView* Texture2D = static_cast<FOpenGLTextureUnorderedAccessView*>(Texture);
FIntVector Size = Texture2D->TextureRHI->GetSizeXYZ();
if (Texture->IsLayered())
{
ClearUAVShader_T<EClearReplacementResourceType::Texture3D, EClearReplacementValueType::Float, 4, false>(RHICmdList, UnorderedAccessViewRHI, Size.X, Size.Y, Size.Z, *reinterpret_cast<const float(*)[4]>(&Values));
}
else
{
ClearUAVShader_T<EClearReplacementResourceType::Texture2D, EClearReplacementValueType::Float, 4, false>(RHICmdList, UnorderedAccessViewRHI, Size.X, Size.Y, Size.Z, *reinterpret_cast<const float(*)[4]>(&Values));
}
}
else
{
check(Texture->BufferResource);
{
int32 NumComponents = 0;
uint32 NumElements = 0;
if (Texture->UnrealFormat != 0)
{
NumComponents = GPixelFormats[Texture->UnrealFormat].NumComponents;
NumElements = Texture->GetBufferSize() / GPixelFormats[Texture->UnrealFormat].BlockBytes;
}
else
{
NumElements = Texture->GetBufferSize() / sizeof(float);
NumComponents = 1;
}
switch (NumComponents)
{
case 1:
ClearUAVShader_T<EClearReplacementResourceType::Buffer, EClearReplacementValueType::Float, 1, false>(RHICmdList, UnorderedAccessViewRHI, NumElements, 1, 1, *reinterpret_cast<const float(*)[1]>(&Values));
break;
case 4:
ClearUAVShader_T<EClearReplacementResourceType::Buffer, EClearReplacementValueType::Float, 4, false>(RHICmdList, UnorderedAccessViewRHI, NumElements, 1, 1, *reinterpret_cast<const float(*)[4]>(&Values));
break;
default:
check(false);
};
}
}
}
void FOpenGLDynamicRHI::RHIClearUAVUint(FRHIUnorderedAccessView* UnorderedAccessViewRHI, const FUintVector4& Values)
{
FOpenGLUnorderedAccessView* Texture = ResourceCast(UnorderedAccessViewRHI);
TRHICommandList_RecursiveHazardous<FOpenGLDynamicRHI> RHICmdList(this);
if (Texture->GetBufferSize() == 0)
{
FOpenGLTextureUnorderedAccessView* Texture2D = static_cast<FOpenGLTextureUnorderedAccessView*>(Texture);
FIntVector Size = Texture2D->TextureRHI->GetSizeXYZ();
if (Texture->IsLayered())
{
ClearUAVShader_T<EClearReplacementResourceType::Texture3D, EClearReplacementValueType::Uint32, 4, false>(RHICmdList, UnorderedAccessViewRHI, Size.X, Size.Y, Size.Z, *reinterpret_cast<const uint32(*)[4]>(&Values));
}
else
{
ClearUAVShader_T<EClearReplacementResourceType::Texture2D, EClearReplacementValueType::Uint32, 4, false>(RHICmdList, UnorderedAccessViewRHI, Size.X, Size.Y, Size.Z, *reinterpret_cast<const uint32(*)[4]>(&Values));
}
}
else
{
check(Texture->BufferResource);
{
int32 NumComponents = 0;
uint32 NumElements = 0;
if (Texture->UnrealFormat != 0)
{
NumComponents = GPixelFormats[Texture->UnrealFormat].NumComponents;
NumElements = Texture->GetBufferSize() / GPixelFormats[Texture->UnrealFormat].BlockBytes;
}
else
{
NumElements = Texture->GetBufferSize() / sizeof(uint32);
NumComponents = 1;
}
switch (NumComponents)
{
case 1:
ClearUAVShader_T<EClearReplacementResourceType::Buffer, EClearReplacementValueType::Uint32, 1, false>(RHICmdList, UnorderedAccessViewRHI, NumElements, 1, 1, *reinterpret_cast<const uint32(*)[1]>(&Values));
break;
case 4:
ClearUAVShader_T<EClearReplacementResourceType::Buffer, EClearReplacementValueType::Uint32, 4, false>(RHICmdList, UnorderedAccessViewRHI, NumElements, 1, 1, *reinterpret_cast<const uint32(*)[4]>(&Values));
break;
default:
check(false);
};
}
}
}