/
redgpu
/
ezEngine
Обзор
Документация
Войти
/
redgpu
/
ezEngine
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
dev
Code/Engine/RendererCore/Textures/Texture2DResource.cpp
492 строки
16 KB
Sanakan8472
Render Graph (#1948)
03 июн 2026, 10:19
Не верифицирован
03 июн 2026, 10:19
60bb12c
Код
Авторство
О чём код?
#include <RendererCore/RendererCorePCH.h> #include <Foundation/Configuration/CVar.h> #include <Foundation/Configuration/Startup.h> #include <Foundation/Utilities/AssetFileHeader.h> #include <RendererCore/RenderContext/RenderContext.h> #include <RendererCore/Textures/Texture2DResource.h> #include <RendererCore/Textures/TextureUtils.h> #include <RendererFoundation/Resources/Texture.h> #include <Texture/Image/Formats/DdsFileFormat.h> #include <Texture/Image/Image.h> #include <Texture/ezTexFormat/ezTexFormat.h> // clang-format off EZ_BEGIN_DYNAMIC_REFLECTED_TYPE(ezTexture2DResource, 1, ezRTTIDefaultAllocator<ezTexture2DResource>) EZ_END_DYNAMIC_REFLECTED_TYPE; // clang-format on ezCVarInt cvar_RenderingOffscreenTargetResolution1("Rendering.Offscreen.TargetResolution1", 256, ezCVarFlags::Default, "Configurable render target resolution"); ezCVarInt cvar_RenderingOffscreenTargetResolution2("Rendering.Offscreen.TargetResolution2", 512, ezCVarFlags::Default, "Configurable render target resolution"); EZ_RESOURCE_IMPLEMENT_COMMON_CODE(ezTexture2DResource); ezTexture2DResource::ezTexture2DResource() : ezResource(DoUpdate::OnGraphicsResourceThreads, ezTextureUtils::s_bForceFullQualityAlways ? 1 : 2) { } ezTexture2DResource::ezTexture2DResource(ezResource::DoUpdate ResourceUpdateThread) : ezResource(ResourceUpdateThread, ezTextureUtils::s_bForceFullQualityAlways ? 1 : 2) { } ezResourceLoadDesc ezTexture2DResource::UnloadData(Unload WhatToUnload) { if (m_uiLoadedTextures > 0) { for (ezInt32 r = 0; r < 2; ++r) { --m_uiLoadedTextures; ezGALDevice::GetDefaultDevice()->DestroyTexture(m_hGALTexture[m_uiLoadedTextures]); m_uiMemoryGPU[m_uiLoadedTextures] = 0; if (WhatToUnload == Unload::OneQualityLevel || m_uiLoadedTextures == 0) break; } } if (WhatToUnload == Unload::AllQualityLevels) { ezGALDevice::GetDefaultDevice()->DestroySamplerState(m_hSamplerState); } ezResourceLoadDesc res; res.m_uiQualityLevelsDiscardable = m_uiLoadedTextures; res.m_uiQualityLevelsLoadable = 2 - m_uiLoadedTextures; res.m_State = m_uiLoadedTextures == 0 ? ezResourceState::Unloaded : ezResourceState::Loaded; return res; } void ezTexture2DResource::FillOutDescriptor(ezTexture2DResourceDescriptor& ref_td, const ezImage* pImage, bool bSRGB, ezUInt32 uiNumMipLevels, ezUInt32& out_uiMemoryUsed, ezHybridArray<ezGALSystemMemoryDescription, 32>& ref_initData) { const ezUInt32 uiHighestMipLevel = pImage->GetNumMipLevels() - uiNumMipLevels; const ezGALResourceFormat::Enum format = ezTextureUtils::ImageFormatToGalFormat(pImage->GetImageFormat(), bSRGB); ref_td.m_DescGAL.m_Format = format; ref_td.m_DescGAL.m_uiWidth = pImage->GetWidth(uiHighestMipLevel); ref_td.m_DescGAL.m_uiHeight = pImage->GetHeight(uiHighestMipLevel); ref_td.m_DescGAL.m_uiDepth = pImage->GetDepth(uiHighestMipLevel); ref_td.m_DescGAL.m_uiMipLevelCount = uiNumMipLevels; ref_td.m_DescGAL.m_uiArraySize = pImage->GetNumArrayIndices(); ref_td.m_DescGAL.m_ResourceAccess.m_bImmutable = true; if (ezImageFormat::GetType(pImage->GetImageFormat()) == ezImageFormatType::BLOCK_COMPRESSED) { ref_td.m_DescGAL.m_uiWidth = ezMath::RoundUp(ref_td.m_DescGAL.m_uiWidth, 4); ref_td.m_DescGAL.m_uiHeight = ezMath::RoundUp(ref_td.m_DescGAL.m_uiHeight, 4); } if (ref_td.m_DescGAL.m_uiDepth > 1) ref_td.m_DescGAL.m_Type = ezGALTextureType::Texture3D; if (pImage->GetNumFaces() == 6) ref_td.m_DescGAL.m_Type = ezGALTextureType::TextureCube; if (ref_td.m_DescGAL.m_uiArraySize > 1) { if (ref_td.m_DescGAL.m_Type == ezGALTextureType::TextureCube) { ref_td.m_DescGAL.m_Type = ezGALTextureType::TextureCubeArray; } else if (ref_td.m_DescGAL.m_Type == ezGALTextureType::Texture2D) { ref_td.m_DescGAL.m_Type = ezGALTextureType::Texture2DArray; } else { EZ_ASSERT_NOT_IMPLEMENTED; } } EZ_ASSERT_DEV(pImage->GetNumFaces() == 1 || pImage->GetNumFaces() == 6, "Invalid number of image faces"); out_uiMemoryUsed = 0; ref_initData.Clear(); for (ezUInt32 array_index = 0; array_index < pImage->GetNumArrayIndices(); ++array_index) { for (ezUInt32 face = 0; face < pImage->GetNumFaces(); ++face) { for (ezUInt32 mip = uiHighestMipLevel; mip < pImage->GetNumMipLevels(); ++mip) { ezGALSystemMemoryDescription& id = ref_initData.ExpandAndGetRef(); id.m_pData = pImage->GetSubImageView(mip, face, array_index).GetByteBlobPtr(); if (ezImageFormat::GetType(pImage->GetImageFormat()) == ezImageFormatType::BLOCK_COMPRESSED) { const ezUInt32 uiMemPitchFactor = ezGALResourceFormat::GetBitsPerElement(format) * 4 / 8; id.m_uiRowPitch = ezMath::RoundUp(pImage->GetWidth(mip), 4) * uiMemPitchFactor; } else { id.m_uiRowPitch = static_cast<ezUInt32>(pImage->GetRowPitch(mip)); } EZ_ASSERT_DEV(pImage->GetDepthPitch(mip) < ezMath::MaxValue<ezUInt32>(), "Depth pitch exceeds ezGAL limits."); id.m_uiSlicePitch = static_cast<ezUInt32>(pImage->GetDepthPitch(mip)); out_uiMemoryUsed += id.m_uiSlicePitch; } } } const ezArrayPtr<ezGALSystemMemoryDescription> InitDataPtr(ref_initData); ref_td.m_InitialContent = InitDataPtr; } ezResourceLoadDesc ezTexture2DResource::UpdateContent(ezStreamReader* Stream) { if (Stream == nullptr) { ezResourceLoadDesc res; res.m_uiQualityLevelsDiscardable = 0; res.m_uiQualityLevelsLoadable = 0; res.m_State = ezResourceState::LoadedResourceMissing; return res; } ezTexture2DResourceDescriptor td; ezImage* pImage = nullptr; bool bIsFallback = false; ezTexFormat texFormat; // load image data { Stream->ReadBytes(&pImage, sizeof(ezImage*)); *Stream >> bIsFallback; texFormat.ReadHeader(*Stream); td.m_SamplerDesc.m_AddressU = texFormat.m_AddressModeU; td.m_SamplerDesc.m_AddressV = texFormat.m_AddressModeV; td.m_SamplerDesc.m_AddressW = texFormat.m_AddressModeW; } const bool bIsRenderTarget = texFormat.m_iRenderTargetResolutionX != 0; EZ_ASSERT_DEV(!bIsRenderTarget, "Render targets are not supported by regular 2D texture resources"); { const ezUInt32 uiNumMipmapsLowRes = ezTextureUtils::s_bForceFullQualityAlways ? pImage->GetNumMipLevels() : ezMath::Min(pImage->GetNumMipLevels(), 6U); ezUInt32 uiUploadNumMipLevels = 0; bool bCouldLoadMore = false; if (bIsFallback) { if (m_uiLoadedTextures == 0) { // only upload fallback textures, if we don't have any texture data at all, yet bCouldLoadMore = true; uiUploadNumMipLevels = uiNumMipmapsLowRes; } else if (m_uiLoadedTextures == 1) { // ignore this texture entirely, if we already have low res data // but assume we could load a higher resolution version bCouldLoadMore = true; ezLog::Debug("Ignoring fallback texture data, low-res resource data is already loaded."); } else { ezLog::Debug("Ignoring fallback texture data, resource is already fully loaded."); } } else { if (m_uiLoadedTextures == 0) { bCouldLoadMore = uiNumMipmapsLowRes < pImage->GetNumMipLevels(); uiUploadNumMipLevels = uiNumMipmapsLowRes; } else if (m_uiLoadedTextures == 1) { uiUploadNumMipLevels = pImage->GetNumMipLevels(); } else { // ignore the texture, if we already have fully loaded data ezLog::Debug("Ignoring texture data, resource is already fully loaded."); } } if (uiUploadNumMipLevels > 0) { EZ_ASSERT_DEBUG(m_uiLoadedTextures < 2, "Invalid texture upload"); ezTempHybridArray<ezGALSystemMemoryDescription, 32> initData; FillOutDescriptor(td, pImage, texFormat.m_bSRGB, uiUploadNumMipLevels, m_uiMemoryGPU[m_uiLoadedTextures], initData); ezTextureUtils::ConfigureSampler(static_cast<ezTextureFilterSetting::Enum>(texFormat.m_TextureFilter.GetValue()), td.m_SamplerDesc); // ignore its return value here, we build our own CreateResource(std::move(td)); } { ezResourceLoadDesc res; res.m_uiQualityLevelsDiscardable = m_uiLoadedTextures; res.m_uiQualityLevelsLoadable = bCouldLoadMore ? 1 : 0; res.m_State = ezResourceState::Loaded; return res; } } } void ezTexture2DResource::UpdateMemoryUsage(MemoryUsage& out_NewMemoryUsage) { out_NewMemoryUsage.m_uiMemoryCPU = sizeof(ezTexture2DResource); out_NewMemoryUsage.m_uiMemoryGPU = m_uiMemoryGPU[0] + m_uiMemoryGPU[1]; } EZ_RESOURCE_IMPLEMENT_CREATEABLE(ezTexture2DResource, ezTexture2DResourceDescriptor) { ezResourceLoadDesc ret; ret.m_uiQualityLevelsDiscardable = descriptor.m_uiQualityLevelsDiscardable; ret.m_uiQualityLevelsLoadable = descriptor.m_uiQualityLevelsLoadable; ret.m_State = ezResourceState::Loaded; ezGALDevice* pDevice = ezGALDevice::GetDefaultDevice(); m_Type = descriptor.m_DescGAL.m_Type; m_Format = descriptor.m_DescGAL.m_Format; m_uiWidth = descriptor.m_DescGAL.m_uiWidth; m_uiHeight = descriptor.m_DescGAL.m_uiHeight; m_hGALTexture[m_uiLoadedTextures] = pDevice->CreateTexture(descriptor.m_DescGAL, descriptor.m_InitialContent); EZ_ASSERT_DEV(!m_hGALTexture[m_uiLoadedTextures].IsInvalidated(), "Texture Data could not be uploaded to the GPU"); ezStringBuilder name; name.SetFormat("{} ([{}] - {}x{})", GetResourceIdOrDescription(), m_uiLoadedTextures, m_uiWidth, m_uiHeight); pDevice->GetTexture(m_hGALTexture[m_uiLoadedTextures])->SetDebugName(name); if (!m_hSamplerState.IsInvalidated()) { pDevice->DestroySamplerState(m_hSamplerState); } m_hSamplerState = pDevice->CreateSamplerState(descriptor.m_SamplerDesc); EZ_ASSERT_DEV(!m_hSamplerState.IsInvalidated(), "Sampler state error"); ++m_uiLoadedTextures; return ret; } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // TODO (resources): move into separate file // clang-format off EZ_BEGIN_DYNAMIC_REFLECTED_TYPE(ezRenderToTexture2DResource, 1, ezRTTIDefaultAllocator<ezRenderToTexture2DResource>); EZ_END_DYNAMIC_REFLECTED_TYPE; EZ_BEGIN_SUBSYSTEM_DECLARATION(RendererCore, Texture2D) BEGIN_SUBSYSTEM_DEPENDENCIES "ResourceManager" END_SUBSYSTEM_DEPENDENCIES ON_CORESYSTEMS_STARTUP { ezResourceManager::RegisterResourceOverrideType(ezGetStaticRTTI<ezRenderToTexture2DResource>(), [](const ezStringBuilder& sResourceID) -> bool { return sResourceID.HasExtension(".ezBinRenderTarget"); }); } ON_CORESYSTEMS_SHUTDOWN { ezResourceManager::UnregisterResourceOverrideType(ezGetStaticRTTI<ezRenderToTexture2DResource>()); } EZ_END_SUBSYSTEM_DECLARATION; // clang-format on EZ_RESOURCE_IMPLEMENT_COMMON_CODE(ezRenderToTexture2DResource); EZ_RESOURCE_IMPLEMENT_CREATEABLE(ezRenderToTexture2DResource, ezRenderToTexture2DResourceDescriptor) { ezResourceLoadDesc ret; ret.m_uiQualityLevelsDiscardable = 0; ret.m_uiQualityLevelsLoadable = 0; ret.m_State = ezResourceState::Loaded; ezGALDevice* pDevice = ezGALDevice::GetDefaultDevice(); m_Type = ezGALTextureType::Texture2D; m_Format = descriptor.m_Format; m_uiWidth = descriptor.m_uiWidth; m_uiHeight = descriptor.m_uiHeight; ezGALTextureCreationDescription descGAL; descGAL.SetAsRenderTarget(m_uiWidth, m_uiHeight, m_Format, descriptor.m_SampleCount); m_hGALTexture[m_uiLoadedTextures] = pDevice->CreateTexture(descGAL, descriptor.m_InitialContent); EZ_ASSERT_DEV(!m_hGALTexture[m_uiLoadedTextures].IsInvalidated(), "Texture data could not be uploaded to the GPU"); pDevice->GetTexture(m_hGALTexture[m_uiLoadedTextures])->SetDebugName(GetResourceDescription()); if (!m_hSamplerState.IsInvalidated()) { pDevice->DestroySamplerState(m_hSamplerState); } m_hSamplerState = pDevice->CreateSamplerState(descriptor.m_SamplerDesc); EZ_ASSERT_DEV(!m_hSamplerState.IsInvalidated(), "Sampler state error"); ++m_uiLoadedTextures; return ret; } ezResourceLoadDesc ezRenderToTexture2DResource::UnloadData(Unload WhatToUnload) { for (ezInt32 r = 0; r < 2; ++r) { if (!m_hGALTexture[r].IsInvalidated()) { ezGALDevice::GetDefaultDevice()->DestroyTexture(m_hGALTexture[r]); m_hGALTexture[r].Invalidate(); } m_uiMemoryGPU[r] = 0; } m_uiLoadedTextures = 0; if (!m_hSamplerState.IsInvalidated()) { ezGALDevice::GetDefaultDevice()->DestroySamplerState(m_hSamplerState); m_hSamplerState.Invalidate(); } ezResourceLoadDesc res; res.m_uiQualityLevelsDiscardable = m_uiLoadedTextures; res.m_uiQualityLevelsLoadable = 2 - m_uiLoadedTextures; res.m_State = ezResourceState::Unloaded; return res; } ezGALRenderTargetViewHandle ezRenderToTexture2DResource::GetRenderTargetView() const { return ezGALDevice::GetDefaultDevice()->GetDefaultRenderTargetView(m_hGALTexture[0]); } void ezRenderToTexture2DResource::AddRenderView(ezViewHandle hView) { m_RenderViews.PushBack(hView); } void ezRenderToTexture2DResource::RemoveRenderView(ezViewHandle hView) { m_RenderViews.RemoveAndSwap(hView); } const ezDynamicArray<ezViewHandle>& ezRenderToTexture2DResource::GetAllRenderViews() const { return m_RenderViews; } static ezUInt16 GetNextBestResolution(float fRes) { fRes = ezMath::Clamp(fRes, 8.0f, 4096.0f); int mulEight = (int)ezMath::Floor((fRes + 7.9f) / 8.0f); return static_cast<ezUInt16>(mulEight * 8); } ezResourceLoadDesc ezRenderToTexture2DResource::UpdateContent(ezStreamReader* Stream) { if (Stream == nullptr) { ezResourceLoadDesc res; res.m_uiQualityLevelsDiscardable = 0; res.m_uiQualityLevelsLoadable = 0; res.m_State = ezResourceState::LoadedResourceMissing; return res; } ezRenderToTexture2DResourceDescriptor td; ezImage* pImage = nullptr; bool bIsFallback = false; ezTexFormat texFormat; // load image data { Stream->ReadBytes(&pImage, sizeof(ezImage*)); *Stream >> bIsFallback; texFormat.ReadHeader(*Stream); td.m_SamplerDesc.m_AddressU = texFormat.m_AddressModeU; td.m_SamplerDesc.m_AddressV = texFormat.m_AddressModeV; td.m_SamplerDesc.m_AddressW = texFormat.m_AddressModeW; } const bool bIsRenderTarget = texFormat.m_iRenderTargetResolutionX != 0; EZ_ASSERT_DEV(bIsRenderTarget, "Trying to create a RenderToTexture resource from data that is not set up as a render-target"); { EZ_ASSERT_DEV(m_uiLoadedTextures == 0, "not implemented"); if (texFormat.m_iRenderTargetResolutionX == -1) { if (texFormat.m_iRenderTargetResolutionY == 1) { texFormat.m_iRenderTargetResolutionX = GetNextBestResolution(cvar_RenderingOffscreenTargetResolution1 * texFormat.m_fResolutionScale); texFormat.m_iRenderTargetResolutionY = texFormat.m_iRenderTargetResolutionX; } else if (texFormat.m_iRenderTargetResolutionY == 2) { texFormat.m_iRenderTargetResolutionX = GetNextBestResolution(cvar_RenderingOffscreenTargetResolution2 * texFormat.m_fResolutionScale); texFormat.m_iRenderTargetResolutionY = texFormat.m_iRenderTargetResolutionX; } else { EZ_REPORT_FAILURE( "Invalid render target configuration: {0} x {1}", texFormat.m_iRenderTargetResolutionX, texFormat.m_iRenderTargetResolutionY); } } td.m_Format = static_cast<ezGALResourceFormat::Enum>(texFormat.m_GalRenderTargetFormat); td.m_uiWidth = texFormat.m_iRenderTargetResolutionX; td.m_uiHeight = texFormat.m_iRenderTargetResolutionY; ezTextureUtils::ConfigureSampler(static_cast<ezTextureFilterSetting::Enum>(texFormat.m_TextureFilter.GetValue()), td.m_SamplerDesc); m_uiLoadedTextures = 0; CreateResource(std::move(td)); } ezResourceLoadDesc res; res.m_uiQualityLevelsDiscardable = 0; res.m_uiQualityLevelsLoadable = 0; res.m_State = ezResourceState::Loaded; return res; } void ezRenderToTexture2DResource::UpdateMemoryUsage(MemoryUsage& out_NewMemoryUsage) { out_NewMemoryUsage.m_uiMemoryCPU = sizeof(ezRenderToTexture2DResource); out_NewMemoryUsage.m_uiMemoryGPU = m_uiMemoryGPU[0] + m_uiMemoryGPU[1]; } EZ_STATICLINK_FILE(RendererCore, RendererCore_Textures_Texture2DResource);