Font update to dynamic draw per view (#1085)
* Move AtomFont over to use the new per viewport dynamic draw context. Remove scene tracking and listening for bootstrap scene created. Remove build dependency on the Bootstrap gem. Add build dependency on the AtomBridge gem. FFont's are now initialized with a viewport Id. Remove previous DynamicDraw context per scene system. Verify FFont can get a dynamic draw context before attempting initialization. Ensure a render scene exists before attempting font initialization (as a proxy for rendering has begun) * Move AtomFont FFont to use ShaderInputNameIndex's This allowed removing all of the InitFont function as no longer need to query compiled shader info for constant data offsets. * cache the AZ::Name used to find the dynamic draw context rather than recreate it each use
This commit is contained in:
@@ -29,7 +29,8 @@ ly_add_target(
|
||||
Legacy::CryCommon
|
||||
Gem::Atom_RHI.Reflect
|
||||
Gem::Atom_RPI.Public
|
||||
Gem::Atom_Bootstrap.Headers
|
||||
PUBLIC
|
||||
Gem::Atom_AtomBridge.Static
|
||||
)
|
||||
|
||||
################################################################################
|
||||
|
||||
@@ -27,11 +27,14 @@
|
||||
#include <AzFramework/Scene/SceneSystemInterface.h>
|
||||
|
||||
#include <Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h>
|
||||
#include <AtomBridge/PerViewportDynamicDrawInterface.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class FFont;
|
||||
|
||||
static constexpr char AtomFontDynamicDrawContextName[] = "AtomFont";
|
||||
|
||||
|
||||
//! AtomFont is the font system manager.
|
||||
//! AtomFont manages the lifetime of FFont instances, each of which represents an individual font (e.g Courier New Italic)
|
||||
@@ -90,13 +93,6 @@ namespace AZ
|
||||
AzFramework::FontDrawInterface* GetFontDrawInterface(AzFramework::FontId fontId) const override;
|
||||
AzFramework::FontDrawInterface* GetDefaultFontDrawInterface() const override;
|
||||
|
||||
void SceneAboutToBeRemoved(AzFramework::Scene& scene);
|
||||
|
||||
|
||||
// Atom DynamicDraw interface management
|
||||
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> GetOrCreateDynamicDrawForScene(AZ::RPI::Scene* scene);
|
||||
|
||||
|
||||
public:
|
||||
void UnregisterFont(const char* fontName);
|
||||
|
||||
@@ -108,8 +104,6 @@ namespace AZ
|
||||
using FontFamilyMap = AZStd::unordered_map<AZStd::string, AZStd::weak_ptr<FontFamily>>;
|
||||
using FontFamilyReverseLookupMap = AZStd::unordered_map<FontFamily*, FontFamilyMap::iterator>;
|
||||
|
||||
using SceneToDynamicDrawMap = AZStd::unordered_map<AZ::RPI::Scene*, AZ::RPI::Ptr<AZ::RPI::DynamicDrawContext>>;
|
||||
|
||||
private:
|
||||
//! Convenience method for loading fonts
|
||||
IFFont* LoadFont(const char* fontName);
|
||||
@@ -145,9 +139,6 @@ namespace AZ
|
||||
|
||||
int r_persistFontFamilies = 1; //!< Persist fonts for application lifetime to prevent unnecessary work; enabled by default.
|
||||
AZStd::vector<FontFamilyPtr> m_persistedFontFamilies; //!< Stores persisted fonts (if "persist font families" is enabled)
|
||||
|
||||
SceneToDynamicDrawMap m_sceneToDynamicDrawMap;
|
||||
AZStd::shared_mutex m_sceneToDynamicDrawMutex;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -42,11 +42,9 @@
|
||||
#include <Atom/RPI.Public/Scene.h>
|
||||
#include <Atom/RPI.Public/DynamicDraw/DynamicDrawInterface.h>
|
||||
#include <Atom/RPI.Public/ViewportContextBus.h>
|
||||
#include <Atom/RPI.Public/WindowContext.h>
|
||||
#include <Atom/RPI.Public/Image/StreamingImage.h>
|
||||
|
||||
#include <Atom/Bootstrap/DefaultWindowBus.h>
|
||||
#include <Atom/Bootstrap/BootstrapNotificationBus.h>
|
||||
|
||||
struct ISystem;
|
||||
|
||||
namespace AZ
|
||||
@@ -68,7 +66,6 @@ namespace AZ
|
||||
: public IFFont
|
||||
, public AZStd::intrusive_refcount<AZStd::atomic_uint, FontDeleter>
|
||||
, public AzFramework::FontDrawInterface
|
||||
, private AZ::Render::Bootstrap::NotificationBus::Handler
|
||||
{
|
||||
using ref_count = AZStd::intrusive_refcount<AZStd::atomic_uint, FontDeleter>;
|
||||
friend FontDeleter;
|
||||
@@ -168,8 +165,8 @@ namespace AZ
|
||||
|
||||
struct FontShaderData
|
||||
{
|
||||
AZ::RHI::ShaderInputImageIndex m_imageInputIndex;
|
||||
AZ::RHI::ShaderInputConstantIndex m_viewProjInputIndex;
|
||||
AZ::RHI::ShaderInputNameIndex m_imageInputIndex = "m_texture";
|
||||
AZ::RHI::ShaderInputNameIndex m_viewProjInputIndex = "m_worldToProj";
|
||||
};
|
||||
|
||||
public:
|
||||
@@ -230,7 +227,6 @@ namespace AZ
|
||||
|
||||
private:
|
||||
virtual ~FFont();
|
||||
bool InitFont(AZ::RPI::Scene* renderScene);
|
||||
bool InitTexture();
|
||||
bool InitCache();
|
||||
|
||||
@@ -281,8 +277,6 @@ namespace AZ
|
||||
|
||||
void ScaleCoord(const RHI::Viewport& viewport, float& x, float& y) const;
|
||||
|
||||
void OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene) override;
|
||||
|
||||
RPI::WindowContextSharedPtr GetDefaultWindowContext() const;
|
||||
RPI::ViewportContextPtr GetDefaultViewportContext() const;
|
||||
|
||||
@@ -303,6 +297,8 @@ namespace AZ
|
||||
string m_name;
|
||||
string m_curPath;
|
||||
|
||||
AZ::Name m_dynamicDrawContextName = AZ::Name(AZ::AtomFontDynamicDrawContextName);
|
||||
|
||||
FontTexture* m_fontTexture = nullptr;
|
||||
|
||||
size_t m_fontBufferSize = 0;
|
||||
@@ -315,13 +311,6 @@ namespace AZ
|
||||
AtomFont* m_atomFont = nullptr;
|
||||
|
||||
bool m_fontTexDirty = false;
|
||||
enum class InitializationState : AZ::u8
|
||||
{
|
||||
Uninitialized,
|
||||
Initializing,
|
||||
Initialized
|
||||
};
|
||||
AZStd::atomic<InitializationState> m_fontInitializationState = InitializationState::Uninitialized;
|
||||
|
||||
FontEffects m_effects;
|
||||
|
||||
@@ -356,6 +345,7 @@ namespace AZ
|
||||
if (font && font->m_atomFont)
|
||||
{
|
||||
font->m_atomFont->UnregisterFont(font->m_name);
|
||||
font->m_atomFont = nullptr;
|
||||
}
|
||||
|
||||
delete font;
|
||||
|
||||
@@ -354,17 +354,26 @@ AZ::AtomFont::AtomFont(ISystem* system)
|
||||
#endif
|
||||
AZ::Interface<AzFramework::FontQueryInterface>::Register(this);
|
||||
|
||||
m_sceneEventHandler = AzFramework::ISceneSystem::SceneEvent::Handler(
|
||||
[this](AzFramework::ISceneSystem::EventType eventType, const AZStd::shared_ptr<AzFramework::Scene>& scene)
|
||||
// register font per viewport dynamic draw context.
|
||||
static const char* shaderFilepath = "Shaders/SimpleTextured.azshader";
|
||||
AZ::AtomBridge::PerViewportDynamicDraw::Get()->RegisterDynamicDrawContext(
|
||||
AZ::Name(AZ::AtomFontDynamicDrawContextName),
|
||||
[](RPI::Ptr<RPI::DynamicDrawContext> drawContext)
|
||||
{
|
||||
if (eventType == AzFramework::ISceneSystem::EventType::ScenePendingRemoval)
|
||||
{
|
||||
SceneAboutToBeRemoved(*scene);
|
||||
}
|
||||
Data::Instance<RPI::Shader> shader = AZ::RPI::LoadShader(shaderFilepath);
|
||||
AZ::RPI::ShaderOptionList shaderOptions;
|
||||
shaderOptions.push_back(AZ::RPI::ShaderOption(AZ::Name("o_useColorChannels"), AZ::Name("false")));
|
||||
shaderOptions.push_back(AZ::RPI::ShaderOption(AZ::Name("o_clamp"), AZ::Name("true")));
|
||||
drawContext->InitShaderWithVariant(shader, &shaderOptions);
|
||||
drawContext->InitVertexFormat(
|
||||
{
|
||||
{"POSITION", RHI::Format::R32G32B32_FLOAT},
|
||||
{"COLOR", RHI::Format::B8G8R8A8_UNORM},
|
||||
{"TEXCOORD0", RHI::Format::R32G32_FLOAT}
|
||||
});
|
||||
drawContext->EndInit();
|
||||
});
|
||||
auto sceneSystem = AzFramework::SceneSystemInterface::Get();
|
||||
AZ_Assert(sceneSystem, "Font created before the scene system is available.");
|
||||
sceneSystem->ConnectToEvents(m_sceneEventHandler);
|
||||
|
||||
}
|
||||
|
||||
AZ::AtomFont::~AtomFont()
|
||||
@@ -860,52 +869,5 @@ XmlNodeRef AZ::AtomFont::LoadFontFamilyXml(const char* fontFamilyName, string& o
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
void AZ::AtomFont::SceneAboutToBeRemoved(AzFramework::Scene& scene)
|
||||
{
|
||||
AZ::RPI::ScenePtr* rpiScene = scene.FindSubsystem<AZ::RPI::ScenePtr>();
|
||||
if (rpiScene)
|
||||
{
|
||||
AZStd::lock_guard<AZStd::shared_mutex> lock(m_sceneToDynamicDrawMutex);
|
||||
if (auto it = m_sceneToDynamicDrawMap.find(rpiScene->get()); it != m_sceneToDynamicDrawMap.end())
|
||||
{
|
||||
m_sceneToDynamicDrawMap.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> AZ::AtomFont::GetOrCreateDynamicDrawForScene(AZ::RPI::Scene* scene)
|
||||
{
|
||||
static const char* shaderFilepath = "Shaders/SimpleTextured.azshader";
|
||||
|
||||
{
|
||||
// shared lock while reading
|
||||
AZStd::shared_lock<AZStd::shared_mutex> lock(m_sceneToDynamicDrawMutex);
|
||||
|
||||
if (auto it = m_sceneToDynamicDrawMap.find(scene); it != m_sceneToDynamicDrawMap.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
// Create and initialize DynamicDrawContext for font draw
|
||||
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw = RPI::DynamicDrawInterface::Get()->CreateDynamicDrawContext(scene);
|
||||
|
||||
Data::Instance<RPI::Shader> shader = AZ::RPI::LoadShader(shaderFilepath);
|
||||
AZ::RPI::ShaderOptionList shaderOptions;
|
||||
shaderOptions.push_back(AZ::RPI::ShaderOption(AZ::Name("o_useColorChannels"), AZ::Name("false")));
|
||||
shaderOptions.push_back(AZ::RPI::ShaderOption(AZ::Name("o_clamp"), AZ::Name("true")));
|
||||
dynamicDraw->InitShaderWithVariant(shader, &shaderOptions);
|
||||
dynamicDraw->InitVertexFormat({{"POSITION", RHI::Format::R32G32B32_FLOAT}, {"COLOR", RHI::Format::B8G8R8A8_UNORM}, {"TEXCOORD0", RHI::Format::R32G32_FLOAT}});
|
||||
dynamicDraw->EndInit();
|
||||
|
||||
// exclusive lock while writing
|
||||
AZStd::lock_guard<AZStd::shared_mutex> lock(m_sceneToDynamicDrawMutex);
|
||||
m_sceneToDynamicDrawMap.insert(AZStd::make_pair(scene, dynamicDraw));
|
||||
|
||||
return dynamicDraw;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -60,14 +60,7 @@ static const size_t MaxVerts = 8 * 1024; // 2048 quads
|
||||
static const size_t MaxIndices = (MaxVerts * 6) / 4; // 6 indices per quad, 6/4 * MaxVerts
|
||||
static const char DrawList2DPassName[] = "2dpass";
|
||||
|
||||
namespace ShaderInputs
|
||||
{
|
||||
static const char TextureIndexName[] = "m_texture";
|
||||
static const char WorldToProjIndexName[] = "m_worldToProj";
|
||||
static const char SamplerIndexName[] = "m_sampler";
|
||||
}
|
||||
|
||||
AZ::FFont::FFont(AtomFont* atomFont, const char* fontName)
|
||||
AZ::FFont::FFont(AZ::AtomFont* atomFont, const char* fontName)
|
||||
: m_name(fontName)
|
||||
, m_atomFont(atomFont)
|
||||
{
|
||||
@@ -78,9 +71,14 @@ AZ::FFont::FFont(AtomFont* atomFont, const char* fontName)
|
||||
FontEffect* effect = AddEffect("default");
|
||||
effect->AddPass();
|
||||
|
||||
AddRef();
|
||||
// Create cpu memory to cache the font draw data before submit
|
||||
m_vertexBuffer = new SVF_P3F_C4B_T2F[MaxVerts];
|
||||
m_indexBuffer = new u16[MaxIndices];
|
||||
|
||||
AZ::Render::Bootstrap::NotificationBus::Handler::BusConnect();
|
||||
m_vertexCount = 0;
|
||||
m_indexCount = 0;
|
||||
|
||||
AddRef();
|
||||
}
|
||||
|
||||
AZ::RPI::ViewportContextPtr AZ::FFont::GetDefaultViewportContext() const
|
||||
@@ -98,55 +96,10 @@ AZ::RPI::WindowContextSharedPtr AZ::FFont::GetDefaultWindowContext() const
|
||||
return {};
|
||||
}
|
||||
|
||||
bool AZ::FFont::InitFont(AZ::RPI::Scene* renderScene)
|
||||
{
|
||||
if (!renderScene)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto initializationState = InitializationState::Uninitialized;
|
||||
// Do an atomic transition to Initializing if we're in the Uninitialized state.
|
||||
// Otherwise, check the current state.
|
||||
// If we're Initialized, there's no more work to be done, return true to indicate we're good to go.
|
||||
// If we're Initializing (on another thread), return false to let the consumer know it's not safe for us to be used yet.
|
||||
if (!m_fontInitializationState.compare_exchange_strong(initializationState, InitializationState::Initializing))
|
||||
{
|
||||
return initializationState == InitializationState::Initialized;
|
||||
}
|
||||
|
||||
// Create and initialize DynamicDrawContext for font draw
|
||||
AZ::RPI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw = m_atomFont->GetOrCreateDynamicDrawForScene(renderScene);
|
||||
|
||||
// Save draw srg input indices for later use
|
||||
Data::Instance<RPI::ShaderResourceGroup> drawSrg = dynamicDraw->NewDrawSrg();
|
||||
const RHI::ShaderResourceGroupLayout* layout = drawSrg->GetAsset()->GetLayout();
|
||||
|
||||
m_fontShaderData.m_imageInputIndex = layout->FindShaderInputImageIndex(AZ::Name(ShaderInputs::TextureIndexName));
|
||||
AZ_Error("AtomFont::FFont", m_fontShaderData.m_imageInputIndex.IsValid(), "Failed to find shader input constant %s.",
|
||||
ShaderInputs::TextureIndexName);
|
||||
|
||||
m_fontShaderData.m_viewProjInputIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::WorldToProjIndexName));
|
||||
AZ_Error("AtomFont::FFont", m_fontShaderData.m_viewProjInputIndex.IsValid(), "Failed to find shader input constant %s.",
|
||||
ShaderInputs::WorldToProjIndexName);
|
||||
|
||||
// Create cpu memory to cache the font draw data before submit
|
||||
m_vertexBuffer = new SVF_P3F_C4B_T2F[MaxVerts];
|
||||
m_indexBuffer = new u16[MaxIndices];
|
||||
|
||||
m_vertexCount = 0;
|
||||
m_indexCount = 0;
|
||||
|
||||
m_fontInitializationState = InitializationState::Initialized;
|
||||
return true;
|
||||
}
|
||||
|
||||
AZ::FFont::~FFont()
|
||||
{
|
||||
AZ_Assert(m_atomFont == nullptr, "The font should already be unregistered through a call to AZ::FFont::Release()");
|
||||
|
||||
AZ::Render::Bootstrap::NotificationBus::Handler::BusDisconnect();
|
||||
|
||||
delete[] m_vertexBuffer;
|
||||
delete[] m_indexBuffer;
|
||||
|
||||
@@ -303,7 +256,8 @@ void AZ::FFont::DrawStringUInternal(
|
||||
const TextDrawContext& ctx)
|
||||
{
|
||||
// Lazily ensure we're initialized before attempting to render.
|
||||
if (!viewportContext || !InitFont(viewportContext->GetRenderScene().get()))
|
||||
// Validate that there is a render scene before attempting to init.
|
||||
if (!viewportContext || !viewportContext->GetRenderScene())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -323,12 +277,6 @@ void AZ::FFont::DrawStringUInternal(
|
||||
return;
|
||||
}
|
||||
|
||||
// if the font is about to be deleted then m_atomFont can be nullptr
|
||||
if (!m_atomFont)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const bool orthoMode = ctx.m_overrideViewProjMatrices;
|
||||
|
||||
const float viewX = viewport.m_minX;
|
||||
@@ -406,14 +354,17 @@ void AZ::FFont::DrawStringUInternal(
|
||||
|
||||
if (numQuads)
|
||||
{
|
||||
auto dynamicDraw = m_atomFont->GetOrCreateDynamicDrawForScene(viewportContext->GetRenderScene().get());
|
||||
//setup per draw srg
|
||||
auto drawSrg = dynamicDraw->NewDrawSrg();
|
||||
drawSrg->SetConstant(m_fontShaderData.m_viewProjInputIndex, modelViewProjMat);
|
||||
drawSrg->SetImageView(m_fontShaderData.m_imageInputIndex, m_fontStreamingImage->GetImageView());
|
||||
drawSrg->Compile();
|
||||
AZ::RPI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw = AZ::AtomBridge::PerViewportDynamicDraw::Get()->GetDynamicDrawContextForViewport(m_dynamicDrawContextName, viewportContext->GetId());
|
||||
if (dynamicDraw)
|
||||
{
|
||||
//setup per draw srg
|
||||
auto drawSrg = dynamicDraw->NewDrawSrg();
|
||||
drawSrg->SetConstant(m_fontShaderData.m_viewProjInputIndex, modelViewProjMat);
|
||||
drawSrg->SetImageView(m_fontShaderData.m_imageInputIndex, m_fontStreamingImage->GetImageView());
|
||||
drawSrg->Compile();
|
||||
|
||||
dynamicDraw->DrawIndexed(m_vertexBuffer, m_vertexCount, m_indexBuffer, m_indexCount, RHI::IndexFormat::Uint16, drawSrg);
|
||||
dynamicDraw->DrawIndexed(m_vertexBuffer, m_vertexCount, m_indexBuffer, m_indexCount, RHI::IndexFormat::Uint16, drawSrg);
|
||||
}
|
||||
m_indexCount = 0;
|
||||
m_vertexCount = 0;
|
||||
}
|
||||
@@ -694,12 +645,6 @@ uint32_t AZ::FFont::WriteTextQuadsToBuffers(SVF_P2F_C4B_T2F_F4B* verts, uint16_t
|
||||
return numQuadsWritten;
|
||||
}
|
||||
|
||||
// if the font is about to be deleted then m_atomFont can be nullptr
|
||||
if (!m_atomFont)
|
||||
{
|
||||
return numQuadsWritten;
|
||||
}
|
||||
|
||||
SVF_P2F_C4B_T2F_F4B* vertexData = verts;
|
||||
uint16_t* indexData = indices;
|
||||
size_t vertexOffset = 0;
|
||||
@@ -1523,7 +1468,7 @@ bool AZ::FFont::UpdateTexture()
|
||||
{
|
||||
using namespace AZ;
|
||||
|
||||
if (m_fontInitializationState != InitializationState::Initialized || !m_fontImage)
|
||||
if (!m_fontImage)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1591,7 +1536,7 @@ void AZ::FFont::Prepare(const char* str, bool updateTexture, const AtomFont::Gly
|
||||
const bool rerenderGlyphs = m_sizeBehavior == SizeBehavior::Rerender;
|
||||
const AtomFont::GlyphSize usedGlyphSize = rerenderGlyphs ? glyphSize : AtomFont::defaultGlyphSize;
|
||||
bool texUpdateNeeded = m_fontTexture->PreCacheString(str, nullptr, m_sizeRatio, usedGlyphSize, m_fontHintParams) == 1 || m_fontTexDirty;
|
||||
if (m_fontInitializationState == InitializationState::Initialized && updateTexture && texUpdateNeeded && m_fontImage)
|
||||
if (updateTexture && texUpdateNeeded && m_fontImage)
|
||||
{
|
||||
UpdateTexture();
|
||||
m_fontTexDirty = false;
|
||||
@@ -1625,12 +1570,6 @@ void AZ::FFont::ScaleCoord(const RHI::Viewport& viewport, float& x, float& y) co
|
||||
y *= height / WindowScaleHeight;
|
||||
}
|
||||
|
||||
|
||||
void AZ::FFont::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstrapScene)
|
||||
{
|
||||
InitFont(bootstrapScene);
|
||||
}
|
||||
|
||||
static void SetCommonContextFlags(AZ::TextDrawContext& ctx, const AzFramework::TextDrawParameters& params)
|
||||
{
|
||||
if (params.m_hAlign == AzFramework::TextHorizontalAlignment::Center)
|
||||
|
||||
Reference in New Issue
Block a user