From f2ed382cf1ce3ce9a9c40a4b4725eb00bc643211 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 3 Oct 2025 14:23:36 -0700 Subject: [PATCH] New ImmutableCString string class (#9291) ImmutableCString is a string class similar to CString except it's immutable. ImmutableCString occupies 16 bytes instead of 8 for CString. However, ImmutableCString is able to avoid memory allocation when constructed from a string literal, and in that way it us similar to StaticString. ImmutableCString can be auto converted from StaticString. The backend tag tracking is updated to use ImmutableCString and the FrameGraph resource manager us updated to use StaticString. Together these changes significantly cut down heap allocations due to internal tagging. We also add optional tracking to {Immutable}CString. --- .../backend/include/private/backend/Driver.h | 1 + .../include/private/backend/DriverAPI.inc | 8 +- .../include/private/backend/HandleAllocator.h | 13 +- filament/backend/src/HandleAllocator.cpp | 8 +- filament/backend/src/metal/MetalBuffer.h | 2 +- filament/backend/src/metal/MetalDriver.mm | 53 ++--- filament/backend/src/metal/MetalHandles.h | 6 +- filament/backend/src/noop/NoopDriver.cpp | 6 +- filament/backend/src/opengl/OpenGLDriver.cpp | 51 ++--- filament/backend/src/opengl/OpenGLDriver.h | 1 + filament/backend/src/vulkan/VulkanDriver.cpp | 51 ++--- .../src/vulkan/memory/ResourceManager.h | 3 +- filament/backend/src/webgpu/WebGPUDriver.cpp | 53 ++--- filament/backend/src/webgpu/WebGPUDriver.h | 2 +- .../backend/test/test_MemoryMappedBuffer.cpp | 2 +- filament/include/filament/FilamentAPI.h | 12 +- filament/src/FilamentBuilder.cpp | 6 +- filament/src/PostProcessManager.cpp | 6 +- filament/src/PostProcessManager.h | 4 +- filament/src/RendererUtils.cpp | 5 +- filament/src/ResourceAllocator.cpp | 18 +- filament/src/ResourceAllocator.h | 11 +- filament/src/details/BufferObject.cpp | 2 +- filament/src/details/InstanceBuffer.h | 4 +- filament/src/details/Material.cpp | 3 +- filament/src/details/MaterialInstance.cpp | 4 +- filament/src/details/MorphTargetBuffer.cpp | 4 +- filament/src/details/RenderTarget.cpp | 3 +- filament/src/details/SkinningBuffer.cpp | 2 +- filament/src/details/Texture.cpp | 5 +- filament/src/details/VertexBuffer.cpp | 6 +- filament/src/fg/FrameGraph.cpp | 13 +- filament/src/fg/FrameGraph.h | 30 +-- filament/src/fg/FrameGraphResources.cpp | 2 +- filament/src/fg/FrameGraphTexture.cpp | 5 +- filament/src/fg/FrameGraphTexture.h | 4 +- filament/src/fg/PassNode.cpp | 2 +- filament/src/fg/Resource.cpp | 4 +- filament/src/fg/ResourceNode.cpp | 2 +- filament/src/fg/details/PassNode.h | 4 +- filament/src/fg/details/Resource.h | 21 +- filament/test/filament_framegraph_test.cpp | 4 +- libs/utils/CMakeLists.txt | 3 + libs/utils/include/utils/CString.h | 56 +++-- libs/utils/include/utils/ImmutableCString.h | 199 +++++++++++++++++ libs/utils/include/utils/Logger.h | 3 +- libs/utils/include/utils/StaticString.h | 10 +- libs/utils/src/CString.cpp | 44 +++- libs/utils/src/ImmutableCString.cpp | 110 ++++++++++ libs/utils/test/test_ImmutableCString.cpp | 206 ++++++++++++++++++ 50 files changed, 841 insertions(+), 236 deletions(-) create mode 100644 libs/utils/include/utils/ImmutableCString.h create mode 100644 libs/utils/src/ImmutableCString.cpp create mode 100644 libs/utils/test/test_ImmutableCString.cpp diff --git a/filament/backend/include/private/backend/Driver.h b/filament/backend/include/private/backend/Driver.h index 7e7f836be3..1fd585fe90 100644 --- a/filament/backend/include/private/backend/Driver.h +++ b/filament/backend/include/private/backend/Driver.h @@ -27,6 +27,7 @@ #include #include +#include #include #include diff --git a/filament/backend/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc index a4b2a83cab..5a024c2a6f 100644 --- a/filament/backend/include/private/backend/DriverAPI.inc +++ b/filament/backend/include/private/backend/DriverAPI.inc @@ -108,15 +108,15 @@ DECL_DRIVER_API_RETURN(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__), PAIR_ARGS_N(PARAM, ##__VA_ARGS__)) #define DECL_DRIVER_API_TAGGED_R_N(R, N, ...) \ - DECL_DRIVER_API_RETURN(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__, utils::CString&&, tag = {}), \ - PAIR_ARGS_N(PARAM, ##__VA_ARGS__, utils::CString&&, tag)) + DECL_DRIVER_API_RETURN(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__, utils::ImmutableCString&&, tag = {}), \ + PAIR_ARGS_N(PARAM, ##__VA_ARGS__, utils::ImmutableCString&&, tag)) #define DECL_DRIVER_API_SYNCHRONOUS_N(R, N, ...) \ DECL_DRIVER_API_SYNCHRONOUS(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__), PAIR_ARGS_N(PARAM, ##__VA_ARGS__)) #define DECL_DRIVER_API_SYNCHRONOUS_TAGGED_N(R, N, ...) \ - DECL_DRIVER_API_SYNCHRONOUS(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__, utils::CString, tag = {}), \ - PAIR_ARGS_N(PARAM, ##__VA_ARGS__, utils::CString, tag)) + DECL_DRIVER_API_SYNCHRONOUS(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__, utils::ImmutableCString, tag = {}), \ + PAIR_ARGS_N(PARAM, ##__VA_ARGS__, utils::ImmutableCString, tag)) // on some compilers the ##__VA_ARGS__ hack is not supported, so we can't handle 0-parameter APIs // with DECL_DRIVER_API_SYNCHRONOUS_N diff --git a/filament/backend/include/private/backend/HandleAllocator.h b/filament/backend/include/private/backend/HandleAllocator.h index f0fb62434e..3bc9633d3f 100644 --- a/filament/backend/include/private/backend/HandleAllocator.h +++ b/filament/backend/include/private/backend/HandleAllocator.h @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -52,16 +53,16 @@ namespace filament::backend { class DebugTag { public: DebugTag(); - void writePoolHandleTag(HandleBase::HandleId key, utils::CString&& tag) noexcept; - void writeHeapHandleTag(HandleBase::HandleId key, utils::CString&& tag) noexcept; - utils::CString findHandleTag(HandleBase::HandleId key) const noexcept; + void writePoolHandleTag(HandleBase::HandleId key, utils::ImmutableCString&& tag) noexcept; + void writeHeapHandleTag(HandleBase::HandleId key, utils::ImmutableCString&& tag) noexcept; + utils::ImmutableCString findHandleTag(HandleBase::HandleId key) const noexcept; private: // This is used to associate a tag to a handle. mDebugTags is only written the in the main // driver thread, but it can be accessed from any thread, because it's called from handle_cast<> // which is used by synchronous calls. mutable utils::Mutex mDebugTagLock; - tsl::robin_map mDebugTags; + tsl::robin_map mDebugTags; }; /* @@ -222,7 +223,7 @@ public: return static_cast(p); } - utils::CString getHandleTag(HandleBase::HandleId key) const noexcept; + utils::ImmutableCString getHandleTag(HandleBase::HandleId key) const noexcept; template bool is_valid(Handle& handle) { @@ -248,7 +249,7 @@ public: return handle_cast(const_cast&>(handle)); } - void associateTagToHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept { + void associateTagToHandle(HandleBase::HandleId id, utils::ImmutableCString&& tag) noexcept { if (tag.empty()) { return; } diff --git a/filament/backend/src/HandleAllocator.cpp b/filament/backend/src/HandleAllocator.cpp index 794064581a..5eb386d23f 100644 --- a/filament/backend/src/HandleAllocator.cpp +++ b/filament/backend/src/HandleAllocator.cpp @@ -156,7 +156,7 @@ void HandleAllocator::deallocateHandleSlow(HandleBase::HandleId id, template UTILS_NOINLINE -CString HandleAllocator::getHandleTag(HandleBase::HandleId id) const noexcept { +ImmutableCString HandleAllocator::getHandleTag(HandleBase::HandleId id) const noexcept { uint32_t key = id; if (UTILS_LIKELY(isPoolHandle(id))) { // Truncate the age to get the debug tag @@ -172,7 +172,7 @@ DebugTag::DebugTag() { } UTILS_NOINLINE -CString DebugTag::findHandleTag(HandleBase::HandleId key) const noexcept { +ImmutableCString DebugTag::findHandleTag(HandleBase::HandleId key) const noexcept { std::unique_lock const lock(mDebugTagLock); if (auto pos = mDebugTags.find(key); pos != mDebugTags.end()) { return pos->second; @@ -181,7 +181,7 @@ CString DebugTag::findHandleTag(HandleBase::HandleId key) const noexcept { } UTILS_NOINLINE -void DebugTag::writePoolHandleTag(HandleBase::HandleId key, CString&& tag) noexcept { +void DebugTag::writePoolHandleTag(HandleBase::HandleId key, ImmutableCString&& tag) noexcept { // This line is the costly part. In the future, we could potentially use a custom // allocator. std::unique_lock const lock(mDebugTagLock); @@ -190,7 +190,7 @@ void DebugTag::writePoolHandleTag(HandleBase::HandleId key, CString&& tag) noexc } UTILS_NOINLINE -void DebugTag::writeHeapHandleTag(HandleBase::HandleId key, CString&& tag) noexcept { +void DebugTag::writeHeapHandleTag(HandleBase::HandleId key, ImmutableCString&& tag) noexcept { // This line is the costly part. In the future, we could potentially use a custom // allocator. std::unique_lock const lock(mDebugTagLock); diff --git a/filament/backend/src/metal/MetalBuffer.h b/filament/backend/src/metal/MetalBuffer.h index d3368181a7..464bafd623 100644 --- a/filament/backend/src/metal/MetalBuffer.h +++ b/filament/backend/src/metal/MetalBuffer.h @@ -192,7 +192,7 @@ public: void* getCpuBuffer() const noexcept { return mCpuBuffer; } - void setLabel(const utils::CString& label) { + void setLabel(const utils::ImmutableCString& label) { #if FILAMENT_METAL_DEBUG_LABELS if (label.empty()) { return; diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index d6473fad5e..fab963d7a7 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -42,6 +42,7 @@ #include #include #include +#include #include @@ -418,14 +419,14 @@ void MetalDriver::finish(int) { } void MetalDriver::createVertexBufferInfoR(Handle vbih, uint8_t bufferCount, - uint8_t attributeCount, AttributeArray attributes, utils::CString&& tag) { + uint8_t attributeCount, AttributeArray attributes, utils::ImmutableCString&& tag) { construct_handle(vbih, *mContext, bufferCount, attributeCount, attributes); mHandleAllocator.associateTagToHandle(vbih.getId(), std::move(tag)); } void MetalDriver::createVertexBufferR(Handle vbh, - uint32_t vertexCount, Handle vbih, utils::CString&& tag) { + uint32_t vertexCount, Handle vbih, utils::ImmutableCString&& tag) { MetalVertexBufferInfo const* const vbi = handle_cast(vbih); construct_handle(vbh, *mContext, vertexCount, vbi->bufferCount, vbih); mHandleAllocator.associateTagToHandle(vbh.getId(), std::move(tag)); @@ -433,7 +434,7 @@ void MetalDriver::createVertexBufferR(Handle vbh, } void MetalDriver::createIndexBufferR(Handle ibh, ElementType elementType, - uint32_t indexCount, BufferUsage usage, utils::CString&& tag) { + uint32_t indexCount, BufferUsage usage, utils::ImmutableCString&& tag) { auto elementSize = (uint8_t)getElementTypeSize(elementType); auto* indexBuffer = construct_handle(ibh, *mContext, usage, elementSize, indexCount); @@ -446,7 +447,7 @@ void MetalDriver::createIndexBufferR(Handle ibh, ElementType elem } void MetalDriver::createBufferObjectR(Handle boh, uint32_t byteCount, - BufferObjectBinding bindingType, BufferUsage usage, utils::CString&& tag) { + BufferObjectBinding bindingType, BufferUsage usage, utils::ImmutableCString&& tag) { auto* bufferObject = construct_handle(boh, *mContext, bindingType, usage, byteCount); FILAMENT_CHECK_POSTCONDITION(bufferObject->getBuffer()->wasAllocationSuccessful()) @@ -488,7 +489,7 @@ inline const char* stringify(SamplerType samplerType) { void MetalDriver::createTextureR(Handle th, SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, - uint32_t depth, TextureUsage usage, utils::CString&& tag) { + uint32_t depth, TextureUsage usage, utils::ImmutableCString&& tag) { // Clamp sample count to what the device supports. auto& sc = mContext->sampleCountLookup; samples = sc[std::min(MAX_SAMPLE_COUNT, samples)]; @@ -508,7 +509,7 @@ void MetalDriver::createTextureR(Handle th, SamplerType target, uint8 } void MetalDriver::createTextureViewR(Handle th, Handle srch, - uint8_t baseLevel, uint8_t levelCount, utils::CString&& tag) { + uint8_t baseLevel, uint8_t levelCount, utils::ImmutableCString&& tag) { MetalTexture const* src = handle_cast(srch); MetalTexture* texture = construct_handle(th, *mContext, src, baseLevel, levelCount); @@ -519,7 +520,7 @@ void MetalDriver::createTextureViewR(Handle th, Handle src void MetalDriver::createTextureViewSwizzleR(Handle th, Handle srch, backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b, - backend::TextureSwizzle a, utils::CString&& tag) { + backend::TextureSwizzle a, utils::ImmutableCString&& tag) { MetalTexture const* src = handle_cast(srch); MetalTexture* texture = construct_handle(th, *mContext, src, r, g, b, a); mContext->textures.insert(texture); @@ -531,13 +532,13 @@ void MetalDriver::createTextureExternalImage2R(Handle th, backend::SamplerType target, backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, - Platform::ExternalImageHandleRef image, utils::CString&& tag) { + Platform::ExternalImageHandleRef image, utils::ImmutableCString&& tag) { // FIXME: implement createTextureExternalImage2R } void MetalDriver::createTextureExternalImageR(Handle th, backend::SamplerType target, backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, - void* image, utils::CString&& tag) { + void* image, utils::ImmutableCString&& tag) { MetalTexture* texture = construct_handle(th, *mContext, format, width, height, usage, (CVPixelBufferRef) image); mContext->textures.insert(texture); @@ -550,7 +551,7 @@ void MetalDriver::createTextureExternalImageR(Handle th, backend::Sam void MetalDriver::createTextureExternalImagePlaneR(Handle th, backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, - void* image, uint32_t plane, utils::CString&& tag) { + void* image, uint32_t plane, utils::ImmutableCString&& tag) { MetalTexture* texture = construct_handle(th, *mContext, format, width, height, usage, (CVPixelBufferRef) image, plane); mContext->textures.insert(texture); @@ -564,7 +565,7 @@ void MetalDriver::createTextureExternalImagePlaneR(Handle th, void MetalDriver::importTextureR(Handle th, intptr_t i, SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, - uint32_t depth, TextureUsage usage, utils::CString&& tag) { + uint32_t depth, TextureUsage usage, utils::ImmutableCString&& tag) { id metalTexture = (id) CFBridgingRelease((void*) i); FILAMENT_CHECK_PRECONDITION(metalTexture.width == width) << "Imported id width (" << metalTexture.width @@ -588,13 +589,13 @@ void MetalDriver::importTextureR(Handle th, intptr_t i, void MetalDriver::createRenderPrimitiveR(Handle rph, Handle vbh, Handle ibh, - PrimitiveType pt, utils::CString&& tag) { + PrimitiveType pt, utils::ImmutableCString&& tag) { construct_handle(rph); MetalDriver::setRenderPrimitiveBuffer(rph, pt, vbh, ibh); mHandleAllocator.associateTagToHandle(rph.getId(), std::move(tag)); } -void MetalDriver::createProgramR(Handle rph, Program&& program, utils::CString&& tag) { +void MetalDriver::createProgramR(Handle rph, Program&& program, utils::ImmutableCString&& tag) { #if FILAMENT_METAL_DEBUG_LOG auto handleId = rph.getId(); DEBUG_LOG("createProgramR(rph = %d, program = ", handleId); @@ -604,7 +605,7 @@ void MetalDriver::createProgramR(Handle rph, Program&& program, utils mHandleAllocator.associateTagToHandle(rph.getId(), std::move(tag)); } -void MetalDriver::createDefaultRenderTargetR(Handle rth, utils::CString&& tag) { +void MetalDriver::createDefaultRenderTargetR(Handle rth, utils::ImmutableCString&& tag) { construct_handle(rth, mContext); mHandleAllocator.associateTagToHandle(rth.getId(), std::move(tag)); } @@ -612,7 +613,7 @@ void MetalDriver::createDefaultRenderTargetR(Handle rth, utils:: void MetalDriver::createRenderTargetR(Handle rth, TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount, MRT color, - TargetBufferInfo depth, TargetBufferInfo stencil, utils::CString&& tag) { + TargetBufferInfo depth, TargetBufferInfo stencil, utils::ImmutableCString&& tag) { FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext)) << "createRenderTarget must be called outside of a render pass."; // Clamp sample count to what the device supports. @@ -659,14 +660,14 @@ void MetalDriver::createRenderTargetR(Handle rth, mHandleAllocator.associateTagToHandle(rth.getId(), std::move(tag)); } -void MetalDriver::createFenceR(Handle fh, utils::CString&& tag) { +void MetalDriver::createFenceR(Handle fh, utils::ImmutableCString&& tag) { auto* fence = handle_cast(fh); fence->encode(); mHandleAllocator.associateTagToHandle(fh.getId(), std::move(tag)); } void MetalDriver::createSwapChainR(Handle sch, void* nativeWindow, uint64_t flags, - utils::CString&& tag) { + utils::ImmutableCString&& tag) { // TODO: support MSAA swapchain if (UTILS_UNLIKELY(flags & SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER)) { @@ -683,18 +684,18 @@ void MetalDriver::createSwapChainR(Handle sch, void* nativeWindow, } void MetalDriver::createSwapChainHeadlessR(Handle sch, - uint32_t width, uint32_t height, uint64_t flags, utils::CString&& tag) { + uint32_t width, uint32_t height, uint64_t flags, utils::ImmutableCString&& tag) { construct_handle(sch, *mContext, mPlatform, width, height, flags); mHandleAllocator.associateTagToHandle(sch.getId(), std::move(tag)); } -void MetalDriver::createSyncR(Handle sh, utils::CString&& tag) { +void MetalDriver::createSyncR(Handle sh, utils::ImmutableCString&& tag) { // TODO: Ensure sync is active, and then invoke and clear all pending // callbacks. mHandleAllocator.associateTagToHandle(sh.getId(), std::move(tag)); } -void MetalDriver::createTimerQueryR(Handle tqh, utils::CString&& tag) { +void MetalDriver::createTimerQueryR(Handle tqh, utils::ImmutableCString&& tag) { // nothing to do, timer query was constructed in createTimerQueryS mHandleAllocator.associateTagToHandle(tqh.getId(), std::move(tag)); } @@ -733,12 +734,12 @@ const char* toString(DescriptorFlags flags) { } void MetalDriver::createDescriptorSetLayoutR( - Handle dslh, DescriptorSetLayout&& info, utils::CString&& tag) { + Handle dslh, DescriptorSetLayout&& info, utils::ImmutableCString&& tag) { #if FILAMENT_METAL_DEBUG_LOG == 1 const char* labelStr = ""; std::visit([&labelStr](auto&& arg) { using T = std::decay_t; - if constexpr (std::is_same_v || std::is_same_v) { + if constexpr (std::is_same_v || std::is_same_v) { labelStr = arg.c_str(); } }, info.label); @@ -758,7 +759,7 @@ void MetalDriver::createDescriptorSetLayoutR( } void MetalDriver::createDescriptorSetR( - Handle dsh, Handle dslh, utils::CString&& tag) { + Handle dsh, Handle dslh, utils::ImmutableCString&& tag) { DEBUG_LOG("createDescriptorSetR(dsh = %d, dslh = %d)\n", dsh.getId(), dslh.getId()); MetalDescriptorSetLayout* layout = handle_cast(dslh); MetalDescriptorSet* ds = construct_handle(dsh, layout); @@ -1010,11 +1011,11 @@ utils::FixedCapacityVector MetalDriver::getShaderLanguages( return { backend::ShaderLanguage::METAL_LIBRARY, backend::ShaderLanguage::MSL }; } -Handle MetalDriver::createStreamNative(void* stream, utils::CString tag) { +Handle MetalDriver::createStreamNative(void* stream, utils::ImmutableCString tag) { return {}; } -Handle MetalDriver::createStreamAcquired(utils::CString tag) { +Handle MetalDriver::createStreamAcquired(utils::ImmutableCString tag) { return {}; } @@ -2231,7 +2232,7 @@ MemoryMappedBufferHandle MetalDriver::mapBufferS() noexcept { void MetalDriver::mapBufferR(MemoryMappedBufferHandle mmbh, BufferObjectHandle boh, size_t offset, - size_t size, MapBufferAccessFlags access, utils::CString&& tag) { + size_t size, MapBufferAccessFlags access, utils::ImmutableCString&& tag) { construct_handle(mmbh, boh, offset, size, access); mHandleAllocator.associateTagToHandle(mmbh.getId(), std::move(tag)); } diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index 2847c50104..1a0917df14 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -267,7 +267,7 @@ public: static MTLPixelFormat decidePixelFormat(MetalContext* context, TextureFormat format); - void setLabel(const utils::CString& label) { + void setLabel(const utils::ImmutableCString& label) { #if FILAMENT_METAL_DEBUG_LABELS if (label.empty()) { return; @@ -494,7 +494,7 @@ struct MetalDescriptorSet : public HwDescriptorSet { void finalize(MetalDriver* driver); - void setLabel(const utils::CString& l) { + void setLabel(const utils::ImmutableCString& l) { #if FILAMENT_METAL_DEBUG_LABELS if (l.empty()) { return; @@ -527,7 +527,7 @@ struct MetalDescriptorSet : public HwDescriptorSet { std::array cachedBuffer = { nil }; #if FILAMENT_METAL_DEBUG_LABELS - utils::CString label; + utils::ImmutableCString label; #endif }; diff --git a/filament/backend/src/noop/NoopDriver.cpp b/filament/backend/src/noop/NoopDriver.cpp index 3602a54df2..23b09ca349 100644 --- a/filament/backend/src/noop/NoopDriver.cpp +++ b/filament/backend/src/noop/NoopDriver.cpp @@ -21,6 +21,8 @@ #include "noop/NoopDriver.h" #include "CommandStreamDispatcher.h" +#include + #include namespace filament::backend { @@ -135,11 +137,11 @@ void NoopDriver::destroyDescriptorSetLayout(Handle tqh) { void NoopDriver::destroyDescriptorSet(Handle tqh) { } -Handle NoopDriver::createStreamNative(void* nativeStream, utils::CString tag) { +Handle NoopDriver::createStreamNative(void* nativeStream, utils::ImmutableCString tag) { return {}; } -Handle NoopDriver::createStreamAcquired(utils::CString tag) { +Handle NoopDriver::createStreamAcquired(utils::ImmutableCString tag) { return {}; } diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index b62b734285..a313240e47 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -661,7 +662,7 @@ void OpenGLDriver::createVertexBufferInfoR( uint8_t bufferCount, uint8_t attributeCount, AttributeArray attributes, - CString&& tag) { + ImmutableCString&& tag) { DEBUG_MARKER() construct(vbih, bufferCount, attributeCount, attributes); mHandleAllocator.associateTagToHandle(vbih.getId(), std::move(tag)); @@ -671,7 +672,7 @@ void OpenGLDriver::createVertexBufferR( Handle vbh, uint32_t vertexCount, Handle vbih, - CString&& tag) { + ImmutableCString&& tag) { DEBUG_MARKER() construct(vbh, vertexCount, vbih); mHandleAllocator.associateTagToHandle(vbh.getId(), std::move(tag)); @@ -682,7 +683,7 @@ void OpenGLDriver::createIndexBufferR( ElementType const elementType, uint32_t indexCount, BufferUsage const usage, - CString&& tag) { + ImmutableCString&& tag) { DEBUG_MARKER() auto& gl = mContext; @@ -698,7 +699,7 @@ void OpenGLDriver::createIndexBufferR( } void OpenGLDriver::createBufferObjectR(Handle boh, uint32_t byteCount, - BufferObjectBinding bindingType, BufferUsage usage, CString&& tag) { + BufferObjectBinding bindingType, BufferUsage usage, ImmutableCString&& tag) { DEBUG_MARKER() assert_invariant(byteCount > 0); @@ -725,7 +726,7 @@ void OpenGLDriver::createBufferObjectR(Handle boh, uint32_t byte void OpenGLDriver::createRenderPrimitiveR(Handle rph, Handle vbh, Handle ibh, - PrimitiveType const pt, CString&& tag) { + PrimitiveType const pt, ImmutableCString&& tag) { DEBUG_MARKER() auto& gl = mContext; @@ -761,7 +762,7 @@ void OpenGLDriver::createRenderPrimitiveR(Handle rph, mHandleAllocator.associateTagToHandle(rph.getId(), std::move(tag)); } -void OpenGLDriver::createProgramR(Handle ph, Program&& program, CString&& tag) { +void OpenGLDriver::createProgramR(Handle ph, Program&& program, ImmutableCString&& tag) { DEBUG_MARKER() construct(ph, *this, std::move(program)); @@ -871,7 +872,7 @@ void OpenGLDriver::textureStorage(GLTexture* t, void OpenGLDriver::createTextureR(Handle th, SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, - TextureUsage usage, CString&& tag) { + TextureUsage usage, ImmutableCString&& tag) { DEBUG_MARKER() GLenum internalFormat = getInternalFormat(format); @@ -964,7 +965,7 @@ void OpenGLDriver::createTextureR(Handle th, SamplerType target, uint } void OpenGLDriver::createTextureViewR(Handle th, - Handle srch, uint8_t const baseLevel, uint8_t const levelCount, CString&& tag) { + Handle srch, uint8_t const baseLevel, uint8_t const levelCount, ImmutableCString&& tag) { DEBUG_MARKER() GLTexture const* const src = handle_cast(srch); @@ -1012,7 +1013,7 @@ void OpenGLDriver::createTextureViewR(Handle th, void OpenGLDriver::createTextureViewSwizzleR(Handle th, Handle srch, TextureSwizzle const r, TextureSwizzle const g, TextureSwizzle const b, TextureSwizzle const a, - CString&& tag) { + ImmutableCString&& tag) { DEBUG_MARKER() GLTexture const* const src = handle_cast(srch); @@ -1078,7 +1079,7 @@ void OpenGLDriver::createTextureViewSwizzleR(Handle th, Handle th, SamplerType target, TextureFormat format, uint32_t width, uint32_t height, TextureUsage usage, - Platform::ExternalImageHandleRef image, CString&& tag) { + Platform::ExternalImageHandleRef image, ImmutableCString&& tag) { DEBUG_MARKER() usage |= TextureUsage::SAMPLEABLE; @@ -1130,7 +1131,7 @@ void OpenGLDriver::createTextureExternalImage2R(Handle th, SamplerTyp void OpenGLDriver::createTextureExternalImageR(Handle th, SamplerType target, TextureFormat format, uint32_t width, uint32_t height, TextureUsage usage, void* image, - CString&& tag) { + ImmutableCString&& tag) { DEBUG_MARKER() usage |= TextureUsage::SAMPLEABLE; @@ -1181,13 +1182,13 @@ void OpenGLDriver::createTextureExternalImageR(Handle th, SamplerType void OpenGLDriver::createTextureExternalImagePlaneR(Handle th, TextureFormat format, uint32_t width, uint32_t height, TextureUsage usage, - void* image, uint32_t plane, CString&&) { + void* image, uint32_t plane, ImmutableCString&&) { // not relevant for the OpenGL backend } void OpenGLDriver::importTextureR(Handle th, intptr_t const id, SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, - uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage, CString&& tag) { + uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage, ImmutableCString&& tag) { DEBUG_MARKER() auto const& gl = mContext; @@ -1642,7 +1643,7 @@ void OpenGLDriver::renderBufferStorage(GLuint const rbo, GLenum internalformat, } void OpenGLDriver::createDefaultRenderTargetR( - Handle rth, CString&& tag) { + Handle rth, ImmutableCString&& tag) { DEBUG_MARKER() construct(rth, 0, 0); // FIXME: we don't know the width/height @@ -1665,7 +1666,7 @@ void OpenGLDriver::createRenderTargetR(Handle rth, MRT color, TargetBufferInfo depth, TargetBufferInfo stencil, - CString&& tag) { + ImmutableCString&& tag) { DEBUG_MARKER() GLRenderTarget* rt = construct(rth, width, height); @@ -1781,7 +1782,7 @@ void OpenGLDriver::createRenderTargetR(Handle rth, mHandleAllocator.associateTagToHandle(rth.getId(), std::move(tag)); } -void OpenGLDriver::createFenceR(Handle fh, CString&& tag) { +void OpenGLDriver::createFenceR(Handle fh, ImmutableCString&& tag) { DEBUG_MARKER() mHandleAllocator.associateTagToHandle(fh.getId(), std::move(tag)); @@ -1815,7 +1816,7 @@ void OpenGLDriver::createFenceR(Handle fh, CString&& tag) { #endif } -void OpenGLDriver::createSyncR(Handle sh, CString&& tag) { +void OpenGLDriver::createSyncR(Handle sh, ImmutableCString&& tag) { DEBUG_MARKER() GLSyncFence* s = handle_cast(sh); @@ -1834,7 +1835,7 @@ void OpenGLDriver::createSyncR(Handle sh, CString&& tag) { } void OpenGLDriver::createSwapChainR(Handle sch, void* nativeWindow, uint64_t const flags, - CString&& tag) { + ImmutableCString&& tag) { DEBUG_MARKER() GLSwapChain* sc = handle_cast(sch); @@ -1856,7 +1857,7 @@ void OpenGLDriver::createSwapChainR(Handle sch, void* nativeWindow, } void OpenGLDriver::createSwapChainHeadlessR(Handle sch, - uint32_t const width, uint32_t const height, uint64_t const flags, CString&& tag) { + uint32_t const width, uint32_t const height, uint64_t const flags, ImmutableCString&& tag) { DEBUG_MARKER() GLSwapChain* sc = handle_cast(sch); @@ -1878,7 +1879,7 @@ void OpenGLDriver::createSwapChainHeadlessR(Handle sch, mHandleAllocator.associateTagToHandle(sch.getId(), std::move(tag)); } -void OpenGLDriver::createTimerQueryR(Handle tqh, CString&& tag) { +void OpenGLDriver::createTimerQueryR(Handle tqh, ImmutableCString&& tag) { DEBUG_MARKER() GLTimerQuery* tq = handle_cast(tqh); mContext.createTimerQuery(tq); @@ -1886,14 +1887,14 @@ void OpenGLDriver::createTimerQueryR(Handle tqh, CString&& tag) { } void OpenGLDriver::createDescriptorSetLayoutR(Handle dslh, - DescriptorSetLayout&& info, CString&& tag) { + DescriptorSetLayout&& info, ImmutableCString&& tag) { DEBUG_MARKER() construct(dslh, std::move(info)); mHandleAllocator.associateTagToHandle(dslh.getId(), std::move(tag)); } void OpenGLDriver::createDescriptorSetR(Handle dsh, - Handle dslh, CString&& tag) { + Handle dslh, ImmutableCString&& tag) { DEBUG_MARKER() GLDescriptorSetLayout const* dsl = handle_cast(dslh); construct(dsh, mContext, dslh, dsl); @@ -1902,7 +1903,7 @@ void OpenGLDriver::createDescriptorSetR(Handle dsh, void OpenGLDriver::mapBufferR(MemoryMappedBufferHandle mmbh, BufferObjectHandle boh, size_t offset, - size_t size, MapBufferAccessFlags access, CString&& tag) { + size_t size, MapBufferAccessFlags access, ImmutableCString&& tag) { DEBUG_MARKER() construct(mmbh, mContext, mHandleAllocator, boh, offset, size, access); mHandleAllocator.associateTagToHandle(mmbh.getId(), std::move(tag)); @@ -2172,14 +2173,14 @@ void OpenGLDriver::unmapBuffer(MemoryMappedBufferHandle mmbh) { // These are called on the application's thread // ------------------------------------------------------------------------------------------------ -Handle OpenGLDriver::createStreamNative(void* nativeStream, CString tag) { +Handle OpenGLDriver::createStreamNative(void* nativeStream, ImmutableCString tag) { Platform::Stream* stream = mPlatform.createStream(nativeStream); auto handle = initHandle(stream); mHandleAllocator.associateTagToHandle(handle.getId(), std::move(tag)); return handle; } -Handle OpenGLDriver::createStreamAcquired(CString tag) { +Handle OpenGLDriver::createStreamAcquired(ImmutableCString tag) { auto handle = initHandle(); mHandleAllocator.associateTagToHandle(handle.getId(), std::move(tag)); return handle; diff --git a/filament/backend/src/opengl/OpenGLDriver.h b/filament/backend/src/opengl/OpenGLDriver.h index f6899dd8e5..f5ddea1d5a 100644 --- a/filament/backend/src/opengl/OpenGLDriver.h +++ b/filament/backend/src/opengl/OpenGLDriver.h @@ -45,6 +45,7 @@ #include #include #include +#include #include #include diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index a516b031d5..a2b50b14ad 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -37,6 +37,7 @@ #include #include +#include #include #ifndef NDEBUG @@ -524,7 +525,7 @@ void VulkanDriver::finish(int dummy) { void VulkanDriver::createRenderPrimitiveR(Handle rph, Handle vbh, Handle ibh, - PrimitiveType pt, utils::CString&& tag) { + PrimitiveType pt, utils::ImmutableCString&& tag) { FVK_SYSTRACE_SCOPE(); auto vb = resource_ptr::cast(&mResourceManager, vbh); auto ib = resource_ptr::cast(&mResourceManager, ibh); @@ -543,7 +544,7 @@ void VulkanDriver::destroyRenderPrimitive(Handle rph) { } void VulkanDriver::createVertexBufferInfoR(Handle vbih, uint8_t bufferCount, - uint8_t attributeCount, AttributeArray attributes, utils::CString&& tag) { + uint8_t attributeCount, AttributeArray attributes, utils::ImmutableCString&& tag) { FVK_SYSTRACE_SCOPE(); auto vbi = resource_ptr::make(&mResourceManager, vbih, bufferCount, attributeCount, attributes); @@ -561,7 +562,7 @@ void VulkanDriver::destroyVertexBufferInfo(Handle vbih) { } void VulkanDriver::createVertexBufferR(Handle vbh, uint32_t vertexCount, - Handle vbih, utils::CString&& tag) { + Handle vbih, utils::ImmutableCString&& tag) { FVK_SYSTRACE_SCOPE(); auto vbi = resource_ptr::cast(&mResourceManager, vbih); auto vb = resource_ptr::make(&mResourceManager, vbh, mContext, mStagePool, @@ -580,7 +581,7 @@ void VulkanDriver::destroyVertexBuffer(Handle vbh) { } void VulkanDriver::createIndexBufferR(Handle ibh, ElementType elementType, - uint32_t indexCount, BufferUsage usage, utils::CString&& tag) { + uint32_t indexCount, BufferUsage usage, utils::ImmutableCString&& tag) { FVK_SYSTRACE_SCOPE(); auto elementSize = (uint8_t) getElementTypeSize(elementType); auto ib = resource_ptr::make(&mResourceManager, ibh, mContext, mAllocator, @@ -599,7 +600,7 @@ void VulkanDriver::destroyIndexBuffer(Handle ibh) { } void VulkanDriver::createBufferObjectR(Handle boh, uint32_t byteCount, - BufferObjectBinding bindingType, BufferUsage usage, utils::CString&& tag) { + BufferObjectBinding bindingType, BufferUsage usage, utils::ImmutableCString&& tag) { FVK_SYSTRACE_SCOPE(); auto bo = resource_ptr::make(&mResourceManager, boh, mContext, mAllocator, mStagePool, mBufferCache, byteCount, bindingType, usage); @@ -618,7 +619,7 @@ void VulkanDriver::destroyBufferObject(Handle boh) { void VulkanDriver::createTextureR(Handle th, SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, - TextureUsage usage, utils::CString&& tag) { + TextureUsage usage, utils::ImmutableCString&& tag) { FVK_SYSTRACE_SCOPE(); auto texture = resource_ptr::make(&mResourceManager, th, mPlatform->getDevice(), mPlatform->getPhysicalDevice(), mContext, mAllocator, &mResourceManager, &mCommands, @@ -635,7 +636,7 @@ void VulkanDriver::createTextureR(Handle th, SamplerType target, uint } void VulkanDriver::createTextureViewR(Handle th, Handle srch, - uint8_t baseLevel, uint8_t levelCount, utils::CString&& tag) { + uint8_t baseLevel, uint8_t levelCount, utils::ImmutableCString&& tag) { auto src = resource_ptr::cast(&mResourceManager, srch); auto texture = resource_ptr::make(&mResourceManager, th, mPlatform->getDevice(), mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, src, baseLevel, @@ -646,7 +647,7 @@ void VulkanDriver::createTextureViewR(Handle th, Handle sr void VulkanDriver::createTextureViewSwizzleR(Handle th, Handle srch, backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b, - backend::TextureSwizzle a, utils::CString&& tag) { + backend::TextureSwizzle a, utils::ImmutableCString&& tag) { TextureSwizzle const swizzleArray[] = { r, g, b, a }; VkComponentMapping const swizzle = fvkutils::getSwizzleMap(swizzleArray); auto src = resource_ptr::cast(&mResourceManager, srch); @@ -658,7 +659,7 @@ void VulkanDriver::createTextureViewSwizzleR(Handle th, Handle th, backend::SamplerType target, backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, - Platform::ExternalImageHandleRef externalImage, utils::CString&& tag) { + Platform::ExternalImageHandleRef externalImage, utils::ImmutableCString&& tag) { FVK_SYSTRACE_SCOPE(); auto metadata = mPlatform->extractExternalImageMetadata(externalImage); @@ -709,7 +710,7 @@ void VulkanDriver::createTextureExternalImage2R(Handle th, backend::S void VulkanDriver::createTextureExternalImageR(Handle th, backend::SamplerType target, backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, - void* externalImage, utils::CString&& tag) { + void* externalImage, utils::ImmutableCString&& tag) { FVK_SYSTRACE_SCOPE(); assert_invariant(false && "Not supported in Vulkan backend"); // not supported in this backend @@ -717,14 +718,14 @@ void VulkanDriver::createTextureExternalImageR(Handle th, backend::Sa void VulkanDriver::createTextureExternalImagePlaneR(Handle th, backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, - void* image, uint32_t plane, utils::CString&& tag) { + void* image, uint32_t plane, utils::ImmutableCString&& tag) { assert_invariant(false && "Not supported in Vulkan backend"); } void VulkanDriver::importTextureR(Handle th, intptr_t id, SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, - TextureUsage usage, utils::CString&& tag) { + TextureUsage usage, utils::ImmutableCString&& tag) { // not supported in this backend assert_invariant(false && "Not supported in Vulkan backend"); mResourceManager.associateHandle(th.getId(), std::move(tag)); @@ -740,7 +741,7 @@ void VulkanDriver::destroyTexture(Handle th) { mExternalImageManager.removeExternallySampledTexture(texture); } -void VulkanDriver::createProgramR(Handle ph, Program&& program, utils::CString&& tag) { +void VulkanDriver::createProgramR(Handle ph, Program&& program, utils::ImmutableCString&& tag) { FVK_SYSTRACE_SCOPE(); auto vprogram = resource_ptr::make(&mResourceManager, ph, mPlatform->getDevice(), program); @@ -756,7 +757,7 @@ void VulkanDriver::destroyProgram(Handle ph) { vprogram.dec(); } -void VulkanDriver::createDefaultRenderTargetR(Handle rth, utils::CString&& tag) { +void VulkanDriver::createDefaultRenderTargetR(Handle rth, utils::ImmutableCString&& tag) { assert_invariant(mDefaultRenderTarget); // Default render target should already exist. auto renderTarget = resource_ptr::make(&mResourceManager, rth, @@ -769,7 +770,7 @@ void VulkanDriver::createDefaultRenderTargetR(Handle rth, utils: void VulkanDriver::createRenderTargetR(Handle rth, TargetBufferFlags targets, uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount, MRT color, TargetBufferInfo depth, TargetBufferInfo stencil, - utils::CString&& tag) { + utils::ImmutableCString&& tag) { FVK_SYSTRACE_SCOPE(); @@ -846,7 +847,7 @@ void VulkanDriver::destroyRenderTarget(Handle rth) { } } -void VulkanDriver::createFenceR(Handle fh, utils::CString&& tag) { +void VulkanDriver::createFenceR(Handle fh, utils::ImmutableCString&& tag) { VulkanCommandBuffer* cmdbuf; if (mCurrentRenderPass.commandBuffer) { cmdbuf = mCurrentRenderPass.commandBuffer; @@ -861,7 +862,7 @@ void VulkanDriver::createFenceR(Handle fh, utils::CString&& tag) { mResourceManager.associateHandle(fh.getId(), std::move(tag)); } -void VulkanDriver::createSyncR(Handle sh, utils::CString&& tag) { +void VulkanDriver::createSyncR(Handle sh, utils::ImmutableCString&& tag) { auto sync = resource_ptr::cast(&mResourceManager, sh); VkFence fence = VK_NULL_HANDLE; std::shared_ptr fenceStatus; @@ -892,7 +893,7 @@ void VulkanDriver::createSyncR(Handle sh, utils::CString&& tag) { } void VulkanDriver::createSwapChainR(Handle sch, void* nativeWindow, uint64_t flags, - utils::CString&& tag) { + utils::ImmutableCString&& tag) { FVK_SYSTRACE_SCOPE(); // Running gc() to guard against an edge case where the old swapchains need to have been // destroyed before the new swapchain can be created. Otherwise, we would fail @@ -921,7 +922,7 @@ void VulkanDriver::createSwapChainR(Handle sch, void* nativeWindow, } void VulkanDriver::createSwapChainHeadlessR(Handle sch, uint32_t width, - uint32_t height, uint64_t flags, utils::CString&& tag) { + uint32_t height, uint64_t flags, utils::ImmutableCString&& tag) { if ((flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0 && !isSRGBSwapChainSupported()) { FVK_LOGW << "sRGB swapchain requested, but Platform does not support it"; flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE); @@ -934,13 +935,13 @@ void VulkanDriver::createSwapChainHeadlessR(Handle sch, uint32_t wi mResourceManager.associateHandle(sch.getId(), std::move(tag)); } -void VulkanDriver::createTimerQueryR(Handle tqh, utils::CString&& tag) { +void VulkanDriver::createTimerQueryR(Handle tqh, utils::ImmutableCString&& tag) { // nothing to do, timer query was constructed in createTimerQueryS mResourceManager.associateHandle(tqh.getId(), std::move(tag)); } void VulkanDriver::createDescriptorSetLayoutR(Handle dslh, - backend::DescriptorSetLayout&& info, utils::CString&& tag) { + backend::DescriptorSetLayout&& info, utils::ImmutableCString&& tag) { FVK_SYSTRACE_SCOPE(); auto layout = mDescriptorSetLayoutCache.createLayout(dslh, std::move(info)); layout.inc(); @@ -948,7 +949,7 @@ void VulkanDriver::createDescriptorSetLayoutR(Handle dslh } void VulkanDriver::createDescriptorSetR(Handle dsh, - Handle dslh, utils::CString&& tag) { + Handle dslh, utils::ImmutableCString&& tag) { FVK_SYSTRACE_SCOPE(); fvkmemory::resource_ptr layout = fvkmemory::resource_ptr::cast(&mResourceManager, dslh); @@ -964,7 +965,7 @@ void VulkanDriver::createDescriptorSetR(Handle dsh, void VulkanDriver::mapBufferR(MemoryMappedBufferHandle mmbh, BufferObjectHandle boh, size_t offset, - size_t size, MapBufferAccessFlags access, utils::CString&& tag) { + size_t size, MapBufferAccessFlags access, utils::ImmutableCString&& tag) { FVK_SYSTRACE_SCOPE(); auto mmb = resource_ptr::make(&mResourceManager, mmbh, boh, offset, size, access); @@ -1131,11 +1132,11 @@ void VulkanDriver::destroyDescriptorSet(Handle dsh) { } } -Handle VulkanDriver::createStreamNative(void* nativeStream, utils::CString tag) { +Handle VulkanDriver::createStreamNative(void* nativeStream, utils::ImmutableCString tag) { return {}; } -Handle VulkanDriver::createStreamAcquired(utils::CString tag) { +Handle VulkanDriver::createStreamAcquired(utils::ImmutableCString tag) { return {}; } diff --git a/filament/backend/src/vulkan/memory/ResourceManager.h b/filament/backend/src/vulkan/memory/ResourceManager.h index 97dfb53358..12a6ade19d 100644 --- a/filament/backend/src/vulkan/memory/ResourceManager.h +++ b/filament/backend/src/vulkan/memory/ResourceManager.h @@ -24,6 +24,7 @@ #include #include +#include namespace filament::backend::fvkmemory { @@ -36,7 +37,7 @@ public: return mHandleAllocatorImpl.allocate(); } - inline void associateHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept { + inline void associateHandle(HandleBase::HandleId id, utils::ImmutableCString&& tag) noexcept { mHandleAllocatorImpl.associateTagToHandle(id, std::move(tag)); } diff --git a/filament/backend/src/webgpu/WebGPUDriver.cpp b/filament/backend/src/webgpu/WebGPUDriver.cpp index 5f1bdab8e9..6efa6ee972 100644 --- a/filament/backend/src/webgpu/WebGPUDriver.cpp +++ b/filament/backend/src/webgpu/WebGPUDriver.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -451,7 +452,7 @@ Handle WebGPUDriver::createTextureExternalImagePlaneS() noexcept { // ------------------------------------------------------------------------------------------------ void WebGPUDriver::createSwapChainR(Handle sch, void* nativeWindow, - const uint64_t flags, utils::CString&& tag) { + const uint64_t flags, utils::ImmutableCString&& tag) { FWGPU_SYSTRACE_SCOPE(); // TODO: support MSAA swapchain @@ -481,7 +482,7 @@ void WebGPUDriver::createSwapChainR(Handle sch, void* nativeWindow, } void WebGPUDriver::createSwapChainHeadlessR(Handle sch, uint32_t width, - uint32_t height, uint64_t flags, utils::CString&& tag) { + uint32_t height, uint64_t flags, utils::ImmutableCString&& tag) { wgpu::Extent2D extent = { .width = width, .height = height }; mSwapChain = constructHandle(sch, extent, mAdapter, mDevice, flags); @@ -494,7 +495,7 @@ void WebGPUDriver::createSwapChainHeadlessR(Handle sch, uint32_t wi void WebGPUDriver::createVertexBufferInfoR(Handle vertexBufferInfoHandle, const uint8_t bufferCount, const uint8_t attributeCount, const AttributeArray attributes, - utils::CString&& tag) { + utils::ImmutableCString&& tag) { FWGPU_SYSTRACE_SCOPE(); constructHandle(vertexBufferInfoHandle, bufferCount, attributeCount, attributes, mDeviceLimits); @@ -503,7 +504,7 @@ void WebGPUDriver::createVertexBufferInfoR(Handle vertexBuff void WebGPUDriver::createVertexBufferR(Handle vertexBufferHandle, const uint32_t vertexCount, Handle vertexBufferInfoHandle, - utils::CString&& tag) { + utils::ImmutableCString&& tag) { FWGPU_SYSTRACE_SCOPE(); const auto vertexBufferInfo = handleCast(vertexBufferInfoHandle); constructHandle(vertexBufferHandle, vertexCount, @@ -513,7 +514,7 @@ void WebGPUDriver::createVertexBufferR(Handle vertexBufferHandle void WebGPUDriver::createIndexBufferR(Handle indexBufferHandle, const ElementType elementType, const uint32_t indexCount, const BufferUsage usage, - utils::CString&& tag) { + utils::ImmutableCString&& tag) { FWGPU_SYSTRACE_SCOPE(); const auto elementSize = static_cast(getElementTypeSize(elementType)); constructHandle(indexBufferHandle, mDevice, elementSize, indexCount); @@ -522,7 +523,7 @@ void WebGPUDriver::createIndexBufferR(Handle indexBufferHandle, void WebGPUDriver::createBufferObjectR(Handle bufferObjectHandle, const uint32_t byteCount, const BufferObjectBinding bindingType, const BufferUsage usage, - utils::CString&& tag) { + utils::ImmutableCString&& tag) { FWGPU_SYSTRACE_SCOPE(); constructHandle(bufferObjectHandle, mDevice, bindingType, byteCount); setDebugTag(bufferObjectHandle.getId(), std::move(tag)); @@ -531,7 +532,7 @@ void WebGPUDriver::createBufferObjectR(Handle bufferObjectHandle void WebGPUDriver::createTextureR(Handle textureHandle, const SamplerType target, const uint8_t levels, const TextureFormat format, const uint8_t samples, const uint32_t width, const uint32_t height, const uint32_t depth, - const TextureUsage usage, utils::CString&& tag) { + const TextureUsage usage, utils::ImmutableCString&& tag) { FWGPU_SYSTRACE_SCOPE(); constructHandle(textureHandle, target, levels, format, samples, width, height, depth, usage, mDevice); @@ -540,7 +541,7 @@ void WebGPUDriver::createTextureR(Handle textureHandle, const Sampler void WebGPUDriver::createTextureViewR(Handle textureHandle, Handle sourceTextureHandle, const uint8_t baseLevel, const uint8_t levelCount, - utils::CString&& tag) { + utils::ImmutableCString&& tag) { auto source = handleCast(sourceTextureHandle); constructHandle(textureHandle, source, baseLevel, levelCount); @@ -551,7 +552,7 @@ void WebGPUDriver::createTextureViewR(Handle textureHandle, void WebGPUDriver::createTextureViewSwizzleR(Handle textureHandle, Handle sourceTextureHandle, const backend::TextureSwizzle r, const backend::TextureSwizzle g, const backend::TextureSwizzle b, - const backend::TextureSwizzle a, utils::CString&& tag) { + const backend::TextureSwizzle a, utils::ImmutableCString&& tag) { if (!isTextureSwizzleSupported()) { FWGPU_LOGW << "WebGPUDriver::createTextureViewSwizzleR called while texture swizzling is " @@ -578,7 +579,7 @@ void WebGPUDriver::createTextureViewSwizzleR(Handle textureHandle, void WebGPUDriver::createTextureExternalImage2R(Handle textureHandle, const backend::SamplerType target, const backend::TextureFormat format, const uint32_t width, const uint32_t height, const backend::TextureUsage usage, - Platform::ExternalImageHandleRef externalImage, utils::CString&& tag) { + Platform::ExternalImageHandleRef externalImage, utils::ImmutableCString&& tag) { FWGPU_SYSTRACE_SCOPE(); PANIC_POSTCONDITION("External WebGPU Texture is not supported"); } @@ -586,27 +587,27 @@ void WebGPUDriver::createTextureExternalImage2R(Handle textureHandle, void WebGPUDriver::createTextureExternalImageR(Handle textureHandle, const backend::SamplerType target, const backend::TextureFormat format, const uint32_t width, const uint32_t height, const backend::TextureUsage usage, - void* externalImage, utils::CString&& tag) { + void* externalImage, utils::ImmutableCString&& tag) { FWGPU_SYSTRACE_SCOPE(); PANIC_POSTCONDITION("External WebGPU Texture is not supported"); } void WebGPUDriver::createTextureExternalImagePlaneR(Handle textureHandle, const backend::TextureFormat format, const uint32_t width, const uint32_t height, - const backend::TextureUsage usage, void* image, const uint32_t plane, utils::CString&& tag) { + const backend::TextureUsage usage, void* image, const uint32_t plane, utils::ImmutableCString&& tag) { PANIC_POSTCONDITION("External WebGPU Texture is not supported"); } void WebGPUDriver::importTextureR(Handle textureHandle, const intptr_t id, const SamplerType target, const uint8_t levels, const TextureFormat format, const uint8_t samples, const uint32_t width, const uint32_t height, const uint32_t depth, - const TextureUsage usage, utils::CString&& tag) { + const TextureUsage usage, utils::ImmutableCString&& tag) { PANIC_POSTCONDITION("Import WebGPU Texture is not supported"); } void WebGPUDriver::createRenderPrimitiveR(Handle renderPrimitiveHandle, Handle vertexBufferHandle, Handle indexBufferHandle, - const PrimitiveType primitiveType, utils::CString&& tag) { + const PrimitiveType primitiveType, utils::ImmutableCString&& tag) { FWGPU_SYSTRACE_SCOPE(); assert_invariant(mDevice); const auto renderPrimitive = constructHandle(renderPrimitiveHandle); @@ -619,14 +620,14 @@ void WebGPUDriver::createRenderPrimitiveR(Handle renderPrimit } void WebGPUDriver::createProgramR(Handle programHandle, Program&& program, - utils::CString&& tag) { + utils::ImmutableCString&& tag) { FWGPU_SYSTRACE_SCOPE(); constructHandle(programHandle, mDevice, program); setDebugTag(programHandle.getId(), std::move(tag)); } void WebGPUDriver::createDefaultRenderTargetR(Handle renderTargetHandle, - utils::CString&& tag) { + utils::ImmutableCString&& tag) { assert_invariant(!mDefaultRenderTarget); mDefaultRenderTarget = constructHandle(renderTargetHandle); assert_invariant(mDefaultRenderTarget); @@ -639,7 +640,7 @@ void WebGPUDriver::createDefaultRenderTargetR(Handle renderTarge void WebGPUDriver::createRenderTargetR(Handle renderTargetHandle, const TargetBufferFlags targetFlags, const uint32_t width, const uint32_t height, const uint8_t samples, const uint8_t layerCount, const MRT color, - const TargetBufferInfo depth, const TargetBufferInfo stencil, utils::CString&& tag) { + const TargetBufferInfo depth, const TargetBufferInfo stencil, utils::ImmutableCString&& tag) { FWGPU_SYSTRACE_SCOPE(); constructHandle( renderTargetHandle, width, height, samples, layerCount, color, depth, stencil, @@ -651,7 +652,7 @@ void WebGPUDriver::createRenderTargetR(Handle renderTargetHandle setDebugTag(renderTargetHandle.getId(), std::move(tag)); } -void WebGPUDriver::createFenceR(Handle fenceHandle, utils::CString&& tag) { +void WebGPUDriver::createFenceR(Handle fenceHandle, utils::ImmutableCString&& tag) { // The handle is constructed synchronously in createFenceS. const auto fence = handleCast(fenceHandle); assert_invariant(mQueue); @@ -659,24 +660,24 @@ void WebGPUDriver::createFenceR(Handle fenceHandle, utils::CString&& ta setDebugTag(fenceHandle.getId(), std::move(tag)); } -void WebGPUDriver::createSyncR(Handle syncHandle, utils::CString&& tag) { +void WebGPUDriver::createSyncR(Handle syncHandle, utils::ImmutableCString&& tag) { // TODO: Ensure sync is active, and then invoke and clear all pending // callbacks. setDebugTag(syncHandle.getId(), std::move(tag)); } -void WebGPUDriver::createTimerQueryR(Handle tqh, utils::CString&& tag) {} +void WebGPUDriver::createTimerQueryR(Handle tqh, utils::ImmutableCString&& tag) {} void WebGPUDriver::createDescriptorSetLayoutR( Handle descriptorSetLayoutHandle, - backend::DescriptorSetLayout&& info, utils::CString&& tag) { + backend::DescriptorSetLayout&& info, utils::ImmutableCString&& tag) { FWGPU_SYSTRACE_SCOPE(); constructHandle(descriptorSetLayoutHandle, std::move(info), mDevice); setDebugTag(descriptorSetLayoutHandle.getId(), std::move(tag)); } void WebGPUDriver::createDescriptorSetR(Handle descriptorSetHandle, - Handle descriptorSetLayoutHandle, utils::CString&& tag) { + Handle descriptorSetLayoutHandle, utils::ImmutableCString&& tag) { FWGPU_SYSTRACE_SCOPE(); auto layout = handleCast(descriptorSetLayoutHandle); constructHandle(descriptorSetHandle, layout->getLayout(), @@ -684,13 +685,13 @@ void WebGPUDriver::createDescriptorSetR(Handle descriptorSetHan setDebugTag(descriptorSetHandle.getId(), std::move(tag)); } -Handle WebGPUDriver::createStreamNative(void* nativeStream, utils::CString tag) { +Handle WebGPUDriver::createStreamNative(void* nativeStream, utils::ImmutableCString tag) { return { //todo }; } -Handle WebGPUDriver::createStreamAcquired(utils::CString tag) { +Handle WebGPUDriver::createStreamAcquired(utils::ImmutableCString tag) { return { //todo }; @@ -2100,7 +2101,7 @@ void WebGPUDriver::bindDescriptorSet(Handle descriptorSetHandle .offsets = std::move(offsets) }; } -void WebGPUDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString&& tag) { +void WebGPUDriver::setDebugTag(HandleBase::HandleId handleId, utils::ImmutableCString&& tag) { //todo } @@ -2223,7 +2224,7 @@ MemoryMappedBufferHandle WebGPUDriver::mapBufferS() noexcept { void WebGPUDriver::mapBufferR(MemoryMappedBufferHandle mmbh, BufferObjectHandle boh, size_t offset, - size_t size, MapBufferAccessFlags access, utils::CString&& tag) { + size_t size, MapBufferAccessFlags access, utils::ImmutableCString&& tag) { // TODO: MetalDriver::mapBufferR } diff --git a/filament/backend/src/webgpu/WebGPUDriver.h b/filament/backend/src/webgpu/WebGPUDriver.h index 69110ea20f..d5ade73fec 100644 --- a/filament/backend/src/webgpu/WebGPUDriver.h +++ b/filament/backend/src/webgpu/WebGPUDriver.h @@ -70,7 +70,7 @@ private: ShaderLanguage preferredLanguage) const noexcept final; [[nodiscard]] wgpu::Sampler makeSampler(SamplerParams const& params); [[nodiscard]] static wgpu::AddressMode fWrapModeToWAddressMode(const filament::backend::SamplerWrapMode& fUsage); - void setDebugTag(HandleBase::HandleId handleId, utils::CString&& tag); + void setDebugTag(HandleBase::HandleId handleId, utils::ImmutableCString&& tag); // The platform (e.g. OS) specific aspects of the WebGPU backend are strictly only // handled in the WebGPUPlatform. diff --git a/filament/backend/test/test_MemoryMappedBuffer.cpp b/filament/backend/test/test_MemoryMappedBuffer.cpp index a5983310d0..5227ea8354 100644 --- a/filament/backend/test/test_MemoryMappedBuffer.cpp +++ b/filament/backend/test/test_MemoryMappedBuffer.cpp @@ -176,7 +176,7 @@ protected: // Map the buffer with a specific offset. MemoryMappedBufferHandle const memoryMappedBuffer = api.mapBuffer(bufferObject, mapOffset, vertexDataSize + copyOffset, - MapBufferAccessFlags::WRITE_BIT, utils::CString{ screenshotName }); + MapBufferAccessFlags::WRITE_BIT, utils::ImmutableCString{ screenshotName }); copyData(vertices.data(), vertexDataSize, copyOffset, memoryMappedBuffer, callbackExecuted); diff --git a/filament/include/filament/FilamentAPI.h b/filament/include/filament/FilamentAPI.h index ddfce15745..d59e051d90 100644 --- a/filament/include/filament/FilamentAPI.h +++ b/filament/include/filament/FilamentAPI.h @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include @@ -57,7 +57,7 @@ template using BuilderBase = utils::PrivateImplementation; // This needs to be public because it is used in the following template. -UTILS_PUBLIC void builderMakeName(utils::CString& outName, const char* name, size_t len) noexcept; +UTILS_PUBLIC void builderMakeName(utils::ImmutableCString& outName, const char* name, size_t len) noexcept; template class UTILS_PUBLIC BuilderNameMixin { @@ -73,18 +73,18 @@ public: return static_cast(*this); } - utils::CString const& getName() const noexcept { return mName; } + utils::ImmutableCString const& getName() const noexcept { return mName; } - utils::CString const& getNameOrDefault() const noexcept { + utils::ImmutableCString const& getNameOrDefault() const noexcept { if (const auto& name = getName(); !name.empty()) { return name; } - static const utils::CString sDefaultName = "(none)"; + static const utils::ImmutableCString sDefaultName = "(none)"; return sDefaultName; } private: - utils::CString mName; + utils::ImmutableCString mName; }; } // namespace filament diff --git a/filament/src/FilamentBuilder.cpp b/filament/src/FilamentBuilder.cpp index 67631eb4c9..e1c17ce4da 100644 --- a/filament/src/FilamentBuilder.cpp +++ b/filament/src/FilamentBuilder.cpp @@ -16,16 +16,18 @@ #include +#include + #include namespace filament { -void builderMakeName(utils::CString& outName, const char* name, size_t const len) noexcept { +void builderMakeName(utils::ImmutableCString& outName, const char* name, size_t const len) noexcept { if (!name) { return; } size_t const length = std::min(len, size_t { 128u }); - outName = utils::CString(name, length); + outName = utils::ImmutableCString(name, length); } } // namespace filament diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index 1f214d556d..c110c3d692 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -3637,7 +3637,7 @@ FrameGraphId PostProcessManager::blitDepth(FrameGraph& fg, } FrameGraphId PostProcessManager::resolve(FrameGraph& fg, - const char* outputBufferName, FrameGraphId const input, + utils::StaticString outputBufferName, FrameGraphId const input, FrameGraphTexture::Descriptor outDesc) noexcept { // Don't do anything if we're not a MSAA buffer @@ -3691,7 +3691,7 @@ FrameGraphId PostProcessManager::resolve(FrameGraph& fg, } FrameGraphId PostProcessManager::resolveDepth(FrameGraph& fg, - const char* outputBufferName, FrameGraphId const input, + utils::StaticString outputBufferName, FrameGraphId const input, FrameGraphTexture::Descriptor outDesc) noexcept { // Don't do anything if we're not a MSAA buffer @@ -3752,7 +3752,7 @@ FrameGraphId PostProcessManager::vsmMipmapPass(FrameGraph& fg auto const& depthMipmapPass = fg.addPass("VSM Generate Mipmap Pass", [&](FrameGraph::Builder& builder, auto& data) { - const char* name = builder.getName(input); + utils::StaticString name = builder.getName(input); data.in = builder.sample(input); auto out = builder.createSubresource(data.in, "Mip level", { diff --git a/filament/src/PostProcessManager.h b/filament/src/PostProcessManager.h index 50f2b3e7b2..b731ebd6a3 100644 --- a/filament/src/PostProcessManager.h +++ b/filament/src/PostProcessManager.h @@ -286,13 +286,13 @@ public: // Resolves base level of input and outputs a texture from outDesc. // outDesc with, height, format and samples will be overridden. FrameGraphId resolve(FrameGraph& fg, - const char* outputBufferName, FrameGraphId input, + utils::StaticString outputBufferName, FrameGraphId input, FrameGraphTexture::Descriptor outDesc) noexcept; // Resolves base level of input and outputs a texture from outDesc. // outDesc with, height, format and samples will be overridden. FrameGraphId resolveDepth(FrameGraph& fg, - const char* outputBufferName, FrameGraphId input, + utils::StaticString outputBufferName, FrameGraphId input, FrameGraphTexture::Descriptor outDesc) noexcept; // VSM shadow mipmap pass diff --git a/filament/src/RendererUtils.cpp b/filament/src/RendererUtils.cpp index d5249088c3..e9e1e21f49 100644 --- a/filament/src/RendererUtils.cpp +++ b/filament/src/RendererUtils.cpp @@ -119,8 +119,9 @@ RendererUtils::ColorPassOutput RendererUtils::colorPass( clearDepthFlags = TargetBufferFlags::DEPTH; clearStencilFlags = config.enabledStencilBuffer ? TargetBufferFlags::STENCIL : TargetBufferFlags::NONE; - const char* const textureName = config.enabledStencilBuffer ? - "Depth/Stencil Buffer" : "Depth Buffer"; + utils::StaticString const textureName = config.enabledStencilBuffer ? + utils::StaticString{"Depth/Stencil Buffer"} : + utils::StaticString{"Depth Buffer"}; bool const isES2 = engine.getDriverApi().getFeatureLevel() == FeatureLevel::FEATURE_LEVEL_0; diff --git a/filament/src/ResourceAllocator.cpp b/filament/src/ResourceAllocator.cpp index 70e123928b..8b588c9ee2 100644 --- a/filament/src/ResourceAllocator.cpp +++ b/filament/src/ResourceAllocator.cpp @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include @@ -143,12 +145,12 @@ void ResourceAllocator::terminate() noexcept { } } -RenderTargetHandle ResourceAllocator::createRenderTarget(const char* name, +RenderTargetHandle ResourceAllocator::createRenderTarget(StaticString name, TargetBufferFlags const targetBufferFlags, uint32_t const width, uint32_t const height, uint8_t const samples, uint8_t const layerCount, MRT const color, TargetBufferInfo const depth, TargetBufferInfo const stencil) noexcept { auto handle = mBackend.createRenderTarget(targetBufferFlags, - width, height, samples ? samples : 1u, layerCount, color, depth, stencil, CString(name)); + width, height, samples ? samples : 1u, layerCount, color, depth, stencil, name); return handle; } @@ -156,7 +158,7 @@ void ResourceAllocator::destroyRenderTarget(RenderTargetHandle const h) noexcept mBackend.destroyRenderTarget(h); } -TextureHandle ResourceAllocator::createTexture(const char* name, +TextureHandle ResourceAllocator::createTexture(StaticString name, SamplerType const target, uint8_t const levels, TextureFormat const format, uint8_t samples, uint32_t const width, uint32_t const height, uint32_t const depth, std::array const swizzle, @@ -166,7 +168,7 @@ TextureHandle ResourceAllocator::createTexture(const char* name, samples = samples ? samples : uint8_t(1); using TS = TextureSwizzle; - constexpr const auto defaultSwizzle = std::array{ + constexpr const auto defaultSwizzle = std::array{ TS::CHANNEL_0, TS::CHANNEL_1, TS::CHANNEL_2, TS::CHANNEL_3}; // do we have a suitable texture in the cache? @@ -183,20 +185,20 @@ TextureHandle ResourceAllocator::createTexture(const char* name, } else { // we don't, allocate a new texture and populate the in-use list handle = mBackend.createTexture( - target, levels, format, samples, width, height, depth, usage, CString(name)); + target, levels, format, samples, width, height, depth, usage, name); if (swizzle != defaultSwizzle) { TextureHandle swizzledHandle = mBackend.createTextureViewSwizzle( - handle, swizzle[0], swizzle[1], swizzle[2], swizzle[3], CString(name)); + handle, swizzle[0], swizzle[1], swizzle[2], swizzle[3], name); mBackend.destroyTexture(handle); handle = swizzledHandle; } } } else { handle = mBackend.createTexture( - target, levels, format, samples, width, height, depth, usage, CString(name)); + target, levels, format, samples, width, height, depth, usage, name); if (swizzle != defaultSwizzle) { TextureHandle swizzledHandle = mBackend.createTextureViewSwizzle( - handle, swizzle[0], swizzle[1], swizzle[2], swizzle[3], CString(name)); + handle, swizzle[0], swizzle[1], swizzle[2], swizzle[3], name); mBackend.destroyTexture(handle); handle = swizzledHandle; } diff --git a/filament/src/ResourceAllocator.h b/filament/src/ResourceAllocator.h index 69eca3aa71..9aae5d8375 100644 --- a/filament/src/ResourceAllocator.h +++ b/filament/src/ResourceAllocator.h @@ -25,6 +25,7 @@ #include "backend/DriverApiForward.h" +#include #include #include @@ -53,7 +54,7 @@ protected: class ResourceAllocatorInterface { public: - virtual backend::RenderTargetHandle createRenderTarget(const char* name, + virtual backend::RenderTargetHandle createRenderTarget(utils::StaticString name, backend::TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height, @@ -65,7 +66,7 @@ public: virtual void destroyRenderTarget(backend::RenderTargetHandle h) noexcept = 0; - virtual backend::TextureHandle createTexture(const char* name, backend::SamplerType target, + virtual backend::TextureHandle createTexture(utils::StaticString name, backend::SamplerType target, uint8_t levels,backend::TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, std::array swizzle, @@ -91,7 +92,7 @@ public: void terminate() noexcept; - backend::RenderTargetHandle createRenderTarget(const char* name, + backend::RenderTargetHandle createRenderTarget(utils::StaticString name, backend::TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height, @@ -103,7 +104,7 @@ public: void destroyRenderTarget(backend::RenderTargetHandle h) noexcept override; - backend::TextureHandle createTexture(const char* name, backend::SamplerType target, + backend::TextureHandle createTexture(utils::StaticString name, backend::SamplerType target, uint8_t levels, backend::TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, std::array swizzle, @@ -119,7 +120,7 @@ private: size_t const mCacheMaxAge; struct TextureKey { - const char* name; // doesn't participate in the hash + utils::StaticString name; // doesn't participate in the hash backend::SamplerType target; uint8_t levels; backend::TextureFormat format; diff --git a/filament/src/details/BufferObject.cpp b/filament/src/details/BufferObject.cpp index a3c35f1177..7557753df9 100644 --- a/filament/src/details/BufferObject.cpp +++ b/filament/src/details/BufferObject.cpp @@ -76,7 +76,7 @@ FBufferObject::FBufferObject(FEngine& engine, const Builder& builder) : mByteCount(builder->mByteCount), mBindingType(builder->mBindingType) { FEngine::DriverApi& driver = engine.getDriverApi(); mHandle = driver.createBufferObject(builder->mByteCount, builder->mBindingType, - backend::BufferUsage::STATIC, utils::CString{ builder.getName() }); + backend::BufferUsage::STATIC, utils::ImmutableCString{ builder.getName() }); } void FBufferObject::terminate(FEngine& engine) { diff --git a/filament/src/details/InstanceBuffer.h b/filament/src/details/InstanceBuffer.h index e2563b4196..b1a61358da 100644 --- a/filament/src/details/InstanceBuffer.h +++ b/filament/src/details/InstanceBuffer.h @@ -55,7 +55,7 @@ public: PerRenderableData* buffer, uint32_t index, uint32_t count, math::mat4f const& rootTransform, PerRenderableData const& ubo); - utils::CString const& getName() const noexcept { return mName; } + utils::ImmutableCString const& getName() const noexcept { return mName; } uint32_t getIndex() const noexcept { return mIndex; } @@ -63,7 +63,7 @@ private: friend class RenderableManager; utils::FixedCapacityVector mLocalTransforms; - utils::CString mName; + utils::ImmutableCString mName; uint32_t mInstanceCount; uint32_t mIndex = 0; }; diff --git a/filament/src/details/Material.cpp b/filament/src/details/Material.cpp index 94224a520a..0fa29b964d 100644 --- a/filament/src/details/Material.cpp +++ b/filament/src/details/Material.cpp @@ -479,7 +479,8 @@ void FMaterial::createAndCacheProgram(Program&& p, Variant const variant) const } } - auto const program = driverApi.createProgram(std::move(p), CString{ mDefinition.name }); + auto const program = driverApi.createProgram(std::move(p), + ImmutableCString{ mDefinition.name.c_str_safe() }); assert_invariant(program); mCachedPrograms[variant.key] = program; diff --git a/filament/src/details/MaterialInstance.cpp b/filament/src/details/MaterialInstance.cpp index 0265535cab..88428dee08 100644 --- a/filament/src/details/MaterialInstance.cpp +++ b/filament/src/details/MaterialInstance.cpp @@ -81,7 +81,7 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material, size_t const uboSize = std::max(size_t(16), material->getUniformInterfaceBlock().getSize()); mUniforms = UniformBuffer(uboSize); mUbHandle = driver.createBufferObject(mUniforms.getSize(), BufferObjectBinding::UNIFORM, - BufferUsage::STATIC, utils::CString{ material->getName() }); + BufferUsage::STATIC, utils::ImmutableCString{ material->getName().c_str_safe() }); // set the UBO, always descriptor 0 mDescriptorSet.setBuffer(material->getDescriptorSetLayout(), @@ -148,7 +148,7 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, mUniforms.setUniforms(other->getUniformBuffer()); mUbHandle = driver.createBufferObject(mUniforms.getSize(), BufferObjectBinding::UNIFORM, - BufferUsage::DYNAMIC, CString{ material->getName() }); + BufferUsage::DYNAMIC, ImmutableCString{ material->getName().c_str_safe() }); // set the UBO, always descriptor 0 mDescriptorSet.setBuffer(mMaterial->getDescriptorSetLayout(), diff --git a/filament/src/details/MorphTargetBuffer.cpp b/filament/src/details/MorphTargetBuffer.cpp index cda888c33f..1ec2b35042 100644 --- a/filament/src/details/MorphTargetBuffer.cpp +++ b/filament/src/details/MorphTargetBuffer.cpp @@ -128,7 +128,7 @@ FMorphTargetBuffer::FMorphTargetBuffer(FEngine& engine, const Builder& builder) getHeight(mVertexCount), mCount, TextureUsage::DEFAULT, - utils::CString{ builder.getName() }); + utils::ImmutableCString{ builder.getName() }); mTbHandle = driver.createTexture(SamplerType::SAMPLER_2D_ARRAY, 1, TextureFormat::RGBA16I, 1, @@ -136,7 +136,7 @@ FMorphTargetBuffer::FMorphTargetBuffer(FEngine& engine, const Builder& builder) getHeight(mVertexCount), mCount, TextureUsage::DEFAULT, - utils::CString{ builder.getName() }); + utils::ImmutableCString{ builder.getName() }); } void FMorphTargetBuffer::terminate(FEngine& engine) { diff --git a/filament/src/details/RenderTarget.cpp b/filament/src/details/RenderTarget.cpp index a424df1dc8..c327da18ca 100644 --- a/filament/src/details/RenderTarget.cpp +++ b/filament/src/details/RenderTarget.cpp @@ -221,7 +221,8 @@ FRenderTarget::FRenderTarget(FEngine& engine, const Builder& builder) FEngine::DriverApi& driver = engine.getDriverApi(); mHandle = driver.createRenderTarget(mAttachmentMask, builder.mImpl->mWidth, builder.mImpl->mHeight, builder.mImpl->mSamples, - builder.mImpl->mLayerCount, mrt, dinfo, {}, utils::CString{ builder.getName() }); + builder.mImpl->mLayerCount, mrt, dinfo, {}, + utils::ImmutableCString{ builder.getName() }); } void FRenderTarget::terminate(FEngine& engine) { diff --git a/filament/src/details/SkinningBuffer.cpp b/filament/src/details/SkinningBuffer.cpp index 746be19b33..1146e3d2ff 100644 --- a/filament/src/details/SkinningBuffer.cpp +++ b/filament/src/details/SkinningBuffer.cpp @@ -91,7 +91,7 @@ FSkinningBuffer::FSkinningBuffer(FEngine& engine, const Builder& builder) getPhysicalBoneCount(mBoneCount) * sizeof(PerRenderableBoneUib::BoneData), BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC, - utils::CString{ builder.getName() }); + utils::ImmutableCString{ builder.getName() }); if (builder->mInitialize) { // initialize the bones to identity (before rounding up) diff --git a/filament/src/details/Texture.cpp b/filament/src/details/Texture.cpp index e23e94076f..12587a51cc 100644 --- a/filament/src/details/Texture.cpp +++ b/filament/src/details/Texture.cpp @@ -313,10 +313,7 @@ FTexture::FTexture(FEngine& engine, const Builder& builder) return; } - CString tag{ builder.getName() }; - if (tag.empty()) { - tag = CString{"FTexture"}; - } + ImmutableCString tag{ !builder.getName().empty() ? builder.getName() : "FTexture" }; if (UTILS_LIKELY(!isImported)) { mHandle = driver.createTexture( diff --git a/filament/src/details/VertexBuffer.cpp b/filament/src/details/VertexBuffer.cpp index dcbcc144ba..3251132c35 100644 --- a/filament/src/details/VertexBuffer.cpp +++ b/filament/src/details/VertexBuffer.cpp @@ -281,7 +281,7 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const Builder& builder) mBufferCount, mDeclaredAttributes.count(), mAttributes); mHandle = driver.createVertexBuffer(mVertexCount, mVertexBufferInfoHandle, - utils::CString{ builder.getName() }); + utils::ImmutableCString{ builder.getName() }); // calculate buffer sizes size_t bufferSizes[MAX_VERTEX_BUFFER_COUNT] = {}; @@ -309,7 +309,7 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const Builder& builder) if (!mBufferObjects[i]) { BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i], BufferObjectBinding::VERTEX, BufferUsage::STATIC, - utils::CString{ builder.getName() }); + utils::ImmutableCString{ builder.getName() }); driver.setVertexBufferObject(mHandle, i, bo); mBufferObjects[i] = bo; } @@ -326,7 +326,7 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const Builder& builder) if (!mBufferObjects[i]) { BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i], BufferObjectBinding::VERTEX, BufferUsage::STATIC, - utils::CString{ builder.getName() }); + utils::ImmutableCString{ builder.getName() }); driver.setVertexBufferObject(mHandle, i, bo); mBufferObjects[i] = bo; } diff --git a/filament/src/fg/FrameGraph.cpp b/filament/src/fg/FrameGraph.cpp index 0c9df6e43e..bc9523fd86 100644 --- a/filament/src/fg/FrameGraph.cpp +++ b/filament/src/fg/FrameGraph.cpp @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -54,11 +55,11 @@ void FrameGraph::Builder::sideEffect() noexcept { mPassNode->makeTarget(); } -const char* FrameGraph::Builder::getName(FrameGraphHandle const handle) const noexcept { +utils::StaticString FrameGraph::Builder::getName(FrameGraphHandle const handle) const noexcept { return mFrameGraph.getResource(handle)->name; } -uint32_t FrameGraph::Builder::declareRenderPass(const char* name, +uint32_t FrameGraph::Builder::declareRenderPass(utils::StaticString name, FrameGraphRenderPass::Descriptor const& desc) { // it's safe here to cast to RenderPassNode because we can't be here for a PresentPassNode // also only RenderPassNodes have the concept of render targets. @@ -438,7 +439,7 @@ FrameGraphHandle FrameGraph::forwardResourceInternal(FrameGraphHandle const reso return resourceHandle; } -FrameGraphId FrameGraph::import(char const* name, +FrameGraphId FrameGraph::import(utils::StaticString name, FrameGraphRenderPass::ImportDescriptor const& desc, backend::Handle target) { // create a resource that represents the imported render target @@ -590,13 +591,13 @@ fgviewer::FrameGraphInfo FrameGraph::getFrameGraphInfo(const char *viewName) con template void FrameGraph::present(FrameGraphId input); -template FrameGraphId FrameGraph::create(char const* name, +template FrameGraphId FrameGraph::create(utils::StaticString name, FrameGraphTexture::Descriptor const& desc) noexcept; template FrameGraphId FrameGraph::createSubresource(FrameGraphId parent, - char const* name, FrameGraphTexture::SubResourceDescriptor const& desc) noexcept; + utils::StaticString name, FrameGraphTexture::SubResourceDescriptor const& desc) noexcept; -template FrameGraphId FrameGraph::import(char const* name, +template FrameGraphId FrameGraph::import(utils::StaticString name, FrameGraphTexture::Descriptor const& desc, FrameGraphTexture::Usage usage, FrameGraphTexture const& resource) noexcept; template FrameGraphId FrameGraph::read(PassNode* passNode, diff --git a/filament/src/fg/FrameGraph.h b/filament/src/fg/FrameGraph.h index ece245b13e..1c8e9f9829 100644 --- a/filament/src/fg/FrameGraph.h +++ b/filament/src/fg/FrameGraph.h @@ -73,7 +73,7 @@ public: * @param desc Descriptor for the FrameGraphRenderPass. * @return An index to retrieve the concrete FrameGraphRenderPass in the execute phase. */ - uint32_t declareRenderPass(const char* name, + uint32_t declareRenderPass(utils::StaticString name, FrameGraphRenderPass::Descriptor const& desc); /** @@ -104,7 +104,7 @@ public: * @return A typed resource handle */ template - FrameGraphId create(const char* name, + FrameGraphId create(utils::StaticString name, typename RESOURCE::Descriptor const& desc = {}) noexcept { return mFrameGraph.create(name, desc); } @@ -122,7 +122,7 @@ public: */ template inline FrameGraphId createSubresource(FrameGraphId parent, - const char* name, + utils::StaticString name, typename RESOURCE::SubResourceDescriptor const& desc = {}) noexcept { return mFrameGraph.createSubresource(parent, name, desc); } @@ -191,7 +191,7 @@ public: * @param handle Handle to a virtual resource * @return C string to the name of the resource */ - const char* getName(FrameGraphHandle handle) const noexcept; + utils::StaticString getName(FrameGraphHandle handle) const noexcept; /** @@ -201,7 +201,7 @@ public: * @param desc Descriptor for this resources * @return A typed resource handle */ - FrameGraphId createTexture(const char* name, + FrameGraphId createTexture(utils::StaticString name, FrameGraphTexture::Descriptor const& desc = {}) noexcept { return create(name, desc); } @@ -375,7 +375,7 @@ public: * @return A handle that can be used normally in the frame graph */ template - FrameGraphId import(const char* name, + FrameGraphId import(utils::StaticString name, typename RESOURCE::Descriptor const& desc, typename RESOURCE::Usage usage, const RESOURCE& resource) noexcept; @@ -391,7 +391,7 @@ public: * @param target handle to the concrete FrameGraphRenderPass to import * @return A handle to a FrameGraphTexture */ - FrameGraphId import(const char* name, + FrameGraphId import(utils::StaticString name, FrameGraphRenderPass::ImportDescriptor const& desc, backend::Handle target); @@ -483,12 +483,12 @@ private: void assertValid(FrameGraphHandle handle) const; template - FrameGraphId create(char const* name, + FrameGraphId create(utils::StaticString name, typename RESOURCE::Descriptor const& desc) noexcept; template FrameGraphId createSubresource(FrameGraphId parent, - char const* name, typename RESOURCE::SubResourceDescriptor const& desc) noexcept; + utils::StaticString name, typename RESOURCE::SubResourceDescriptor const& desc) noexcept; template FrameGraphId read(PassNode* passNode, @@ -587,7 +587,7 @@ void FrameGraph::present(FrameGraphId input) { } template -FrameGraphId FrameGraph::create(char const* name, +FrameGraphId FrameGraph::create(utils::StaticString name, typename RESOURCE::Descriptor const& desc) noexcept { VirtualResource* vresource(mArena.make>(name, desc)); return FrameGraphId(addResourceInternal(vresource)); @@ -595,14 +595,14 @@ FrameGraphId FrameGraph::create(char const* name, template FrameGraphId FrameGraph::createSubresource(FrameGraphId parent, - char const* name, typename RESOURCE::SubResourceDescriptor const& desc) noexcept { + utils::StaticString name, typename RESOURCE::SubResourceDescriptor const& desc) noexcept { auto* parentResource = static_cast*>(getResource(parent)); VirtualResource* vresource(mArena.make>(parentResource, name, desc)); return FrameGraphId(addSubResourceInternal(parent, vresource)); } template -FrameGraphId FrameGraph::import(char const* name, +FrameGraphId FrameGraph::import(utils::StaticString name, typename RESOURCE::Descriptor const& desc, typename RESOURCE::Usage usage, RESOURCE const& resource) noexcept { @@ -665,13 +665,13 @@ FrameGraphId FrameGraph::forwardResource(char const* name, extern template void FrameGraph::present(FrameGraphId input); -extern template FrameGraphId FrameGraph::create(char const* name, +extern template FrameGraphId FrameGraph::create(utils::StaticString name, FrameGraphTexture::Descriptor const& desc) noexcept; extern template FrameGraphId FrameGraph::createSubresource(FrameGraphId parent, - char const* name, FrameGraphTexture::SubResourceDescriptor const& desc) noexcept; + utils::StaticString name, FrameGraphTexture::SubResourceDescriptor const& desc) noexcept; -extern template FrameGraphId FrameGraph::import(char const* name, +extern template FrameGraphId FrameGraph::import(utils::StaticString name, FrameGraphTexture::Descriptor const& desc, FrameGraphTexture::Usage usage, FrameGraphTexture const& resource) noexcept; extern template FrameGraphId FrameGraph::read(PassNode* passNode, diff --git a/filament/src/fg/FrameGraphResources.cpp b/filament/src/fg/FrameGraphResources.cpp index 23444393f0..b60aa81299 100644 --- a/filament/src/fg/FrameGraphResources.cpp +++ b/filament/src/fg/FrameGraphResources.cpp @@ -43,7 +43,7 @@ VirtualResource& FrameGraphResources::getResource(FrameGraphHandle const handle) FILAMENT_CHECK_PRECONDITION(hasReadOrWrite) << "Pass \"" << mPassNode.getName() << "\" didn't declare any access to resource \"" - << resource->name << "\""; + << resource->name.c_str() << "\""; assert_invariant(resource->refcount); diff --git a/filament/src/fg/FrameGraphTexture.cpp b/filament/src/fg/FrameGraphTexture.cpp index 03f29ad1f2..d913543d16 100644 --- a/filament/src/fg/FrameGraphTexture.cpp +++ b/filament/src/fg/FrameGraphTexture.cpp @@ -18,11 +18,14 @@ #include "ResourceAllocator.h" +#include + #include namespace filament { -void FrameGraphTexture::create(ResourceAllocatorInterface& resourceAllocator, const char* name, +void FrameGraphTexture::create(ResourceAllocatorInterface& resourceAllocator, + utils::StaticString const name, Descriptor const& descriptor, Usage usage, bool const useProtectedMemory) noexcept { if (useProtectedMemory) { diff --git a/filament/src/fg/FrameGraphTexture.h b/filament/src/fg/FrameGraphTexture.h index cb785bb7b6..29d10b9150 100644 --- a/filament/src/fg/FrameGraphTexture.h +++ b/filament/src/fg/FrameGraphTexture.h @@ -19,6 +19,8 @@ #include "fg/FrameGraphId.h" +#include + #include #include @@ -78,7 +80,7 @@ struct FrameGraphTexture { * @param resourceAllocator resource allocator for textures and such * @param descriptor Descriptor to the resource */ - void create(ResourceAllocatorInterface& resourceAllocator, const char* name, + void create(ResourceAllocatorInterface& resourceAllocator, utils::StaticString name, Descriptor const& descriptor, Usage usage, bool useProtectedMemory) noexcept; /** diff --git a/filament/src/fg/PassNode.cpp b/filament/src/fg/PassNode.cpp index 4efa7cd61d..a25ff290a0 100644 --- a/filament/src/fg/PassNode.cpp +++ b/filament/src/fg/PassNode.cpp @@ -81,7 +81,7 @@ void RenderPassNode::execute(FrameGraphResources const& resources, DriverApi& dr } uint32_t RenderPassNode::declareRenderTarget(FrameGraph& fg, FrameGraph::Builder&, - const char* name, FrameGraphRenderPass::Descriptor const& descriptor) { + utils::StaticString name, FrameGraphRenderPass::Descriptor const& descriptor) { RenderPassData data; data.name = name; diff --git a/filament/src/fg/Resource.cpp b/filament/src/fg/Resource.cpp index be974a0def..6439b95349 100644 --- a/filament/src/fg/Resource.cpp +++ b/filament/src/fg/Resource.cpp @@ -85,7 +85,7 @@ void VirtualResource::neededByPass(PassNode* pNode) noexcept { ImportedRenderTarget::~ImportedRenderTarget() noexcept = default; -ImportedRenderTarget::ImportedRenderTarget(char const* resourceName, +ImportedRenderTarget::ImportedRenderTarget(utils::StaticString resourceName, FrameGraphTexture::Descriptor const& mainAttachmentDesc, FrameGraphRenderPass::ImportDescriptor const& importedDesc, Handle target) @@ -101,7 +101,7 @@ void ImportedRenderTarget::assertConnect(FrameGraphTexture::Usage const u) { FrameGraphTexture::Usage::STENCIL_ATTACHMENT; FILAMENT_CHECK_PRECONDITION(none(u & ~ANY_ATTACHMENT)) - << "Imported render target resource \"" << name + << "Imported render target resource \"" << name.c_str() << "\" can only be used as an attachment (usage=" << utils::to_string(u).c_str() << ')'; } diff --git a/filament/src/fg/ResourceNode.cpp b/filament/src/fg/ResourceNode.cpp index ca75e33d57..1c8da3384e 100644 --- a/filament/src/fg/ResourceNode.cpp +++ b/filament/src/fg/ResourceNode.cpp @@ -65,7 +65,7 @@ ResourceNode* ResourceNode::getAncestorNode(ResourceNode* node) noexcept { } char const* ResourceNode::getName() const noexcept { - return mFrameGraph.getResource(resourceHandle)->name; + return mFrameGraph.getResource(resourceHandle)->name.c_str(); } void ResourceNode::addOutgoingEdge(ResourceEdgeBase* edge) noexcept { diff --git a/filament/src/fg/details/PassNode.h b/filament/src/fg/details/PassNode.h index 9f483e7a6c..66e7c7c9dc 100644 --- a/filament/src/fg/details/PassNode.h +++ b/filament/src/fg/details/PassNode.h @@ -67,7 +67,7 @@ public: class RenderPassData { public: static constexpr size_t ATTACHMENT_COUNT = backend::MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT + 2; - const char* name = {}; + utils::StaticString name{}; FrameGraphRenderPass::Descriptor descriptor; bool imported = false; backend::TargetBufferFlags targetBufferFlags = {}; @@ -88,7 +88,7 @@ public: ~RenderPassNode() noexcept override; uint32_t declareRenderTarget(FrameGraph& fg, FrameGraph::Builder& builder, - const char* name, FrameGraphRenderPass::Descriptor const& descriptor); + utils::StaticString name, FrameGraphRenderPass::Descriptor const& descriptor); RenderPassData const* getRenderPassData(uint32_t id) const noexcept; diff --git a/filament/src/fg/details/Resource.h b/filament/src/fg/details/Resource.h index 7acd0e1595..98c55d9863 100644 --- a/filament/src/fg/details/Resource.h +++ b/filament/src/fg/details/Resource.h @@ -23,6 +23,7 @@ #include "fg/details/DependencyGraph.h" #include +#include namespace filament { class ResourceAllocatorInterface; @@ -49,15 +50,19 @@ class VirtualResource { public: // constants VirtualResource* parent; - const char* const name; + utils::StaticString name; // computed during compile() uint32_t refcount = 0; PassNode* first = nullptr; // pass that needs to instantiate the resource PassNode* last = nullptr; // pass that can destroy the resource - explicit VirtualResource(const char* name) noexcept : parent(this), name(name) { } - VirtualResource(VirtualResource* parent, const char* name) noexcept : parent(parent), name(name) { } + explicit VirtualResource(utils::StaticString const name) noexcept : parent(this), name(name) { + } + + VirtualResource(VirtualResource* parent, + utils::StaticString const name) noexcept : parent(parent), name(name) { + } VirtualResource(VirtualResource const& rhs) noexcept = delete; VirtualResource& operator=(VirtualResource const&) = delete; virtual ~VirtualResource() noexcept; @@ -147,12 +152,12 @@ public: }; UTILS_NOINLINE - Resource(const char* name, Descriptor const& desc) noexcept + Resource(utils::StaticString name, Descriptor const& desc) noexcept : VirtualResource(name), descriptor(desc) { } UTILS_NOINLINE - Resource(Resource* parent, const char* name, SubResourceDescriptor const& desc) noexcept + Resource(Resource* parent, utils::StaticString name, SubResourceDescriptor const& desc) noexcept : VirtualResource(parent, name), descriptor(RESOURCE::generateSubResourceDescriptor(parent->descriptor, desc)), subResourceDescriptor(desc) { @@ -263,7 +268,7 @@ public: using Usage = typename RESOURCE::Usage; UTILS_NOINLINE - ImportedResource(const char* name, Descriptor const& desc, Usage usage, RESOURCE const& rsrc) noexcept + ImportedResource(utils::StaticString name, Descriptor const& desc, Usage usage, RESOURCE const& rsrc) noexcept : Resource(name, desc) { this->resource = rsrc; this->usage = usage; @@ -298,7 +303,7 @@ private: void assertConnect(FrameGraphTexture::Usage u) { FILAMENT_CHECK_PRECONDITION((u & this->usage) == u) << "Requested usage " << utils::to_string(u).c_str() - << " not available on imported resource \"" << this->name << "\" with usage " + << " not available on imported resource \"" << this->name.c_str() << "\" with usage " << utils::to_string(this->usage).c_str(); } }; @@ -310,7 +315,7 @@ public: FrameGraphRenderPass::ImportDescriptor importedDesc; UTILS_NOINLINE - ImportedRenderTarget(const char* name, + ImportedRenderTarget(utils::StaticString name, FrameGraphTexture::Descriptor const& mainAttachmentDesc, FrameGraphRenderPass::ImportDescriptor const& importedDesc, backend::Handle target); diff --git a/filament/test/filament_framegraph_test.cpp b/filament/test/filament_framegraph_test.cpp index 5734c23079..0ae4a42966 100644 --- a/filament/test/filament_framegraph_test.cpp +++ b/filament/test/filament_framegraph_test.cpp @@ -41,7 +41,7 @@ class MockResourceAllocator : public ResourceAllocatorInterface { } disposer; public: - backend::RenderTargetHandle createRenderTarget(const char* name, + backend::RenderTargetHandle createRenderTarget(utils::StaticString name, backend::TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height, @@ -56,7 +56,7 @@ public: void destroyRenderTarget(backend::RenderTargetHandle h) noexcept override { } - backend::TextureHandle createTexture(const char* name, backend::SamplerType target, + backend::TextureHandle createTexture(utils::StaticString name, backend::SamplerType target, uint8_t levels, backend::TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, std::array, diff --git a/libs/utils/CMakeLists.txt b/libs/utils/CMakeLists.txt index 9b013e2015..13f5f76954 100644 --- a/libs/utils/CMakeLists.txt +++ b/libs/utils/CMakeLists.txt @@ -21,6 +21,7 @@ set(DIST_HDRS ${PUBLIC_HDR_DIR}/${TARGET}/compiler.h ${PUBLIC_HDR_DIR}/${TARGET}/compressed_pair.h ${PUBLIC_HDR_DIR}/${TARGET}/CString.h + ${PUBLIC_HDR_DIR}/${TARGET}/ImmutableCString.h ${PUBLIC_HDR_DIR}/${TARGET}/Entity.h ${PUBLIC_HDR_DIR}/${TARGET}/EntityInstance.h ${PUBLIC_HDR_DIR}/${TARGET}/EntityManager.h @@ -72,6 +73,7 @@ set(SRCS src/EntityManager.cpp src/EntityManagerImpl.h src/FixedCapacityVectorBase.cpp + src/ImmutableCString.cpp src/Invocable.cpp src/JobSystem.cpp src/Log.cpp @@ -169,6 +171,7 @@ set(TEST_SRCS test/test_bitset.cpp test/test_CountDownLatch.cpp test/test_CString.cpp + test/test_ImmutableCString.cpp test/test_CyclicBarrier.cpp test/test_Entity.cpp test/test_FixedCapacityVector.cpp diff --git a/libs/utils/include/utils/CString.h b/libs/utils/include/utils/CString.h index f3eabbb265..f591384c75 100644 --- a/libs/utils/include/utils/CString.h +++ b/libs/utils/include/utils/CString.h @@ -53,14 +53,15 @@ struct hashCStrings { template using StringLiteral = const char[N]; -namespace details { - template - constexpr bool is_char_pointer_v = std::is_pointer_v && std::is_same_v>>; -} // namespace details - // ------------------------------------------------------------------------------------------------ class UTILS_PUBLIC CString { + static constexpr bool TRACK_AND_LOG_ALLOCATIONS = false; + + template + static constexpr bool is_char_pointer_v = + std::is_pointer_v && std::is_same_v>>; + public: using value_type = char; using size_type = uint32_t; @@ -72,7 +73,9 @@ public: using iterator = value_type*; using const_iterator = const value_type*; - CString() noexcept {} // NOLINT(modernize-use-equals-default), Ubuntu compiler bug + CString() noexcept { + track(true); + } // Allocates memory and appends a null. This constructor can be used to hold arbitrary data // inside the string (i.e. it can contain nulls or non-ASCII encodings). @@ -86,25 +89,37 @@ public: // Allocates memory and copies traditional C string content. Unlike the above constructor, this // does not allow embedded nulls. This is explicit because this operation is costly. // This is a template to ensure it's not preferred over the string literal constructor below. - template>> + template>> explicit CString(T cstr) : CString(cstr, cstr ? strlen(cstr) : 0) { + track(true); } + // The string can't have NULs in it. template CString(StringLiteral const& other) noexcept // NOLINT(google-explicit-constructor) : CString(other, N - 1) { + track(true); } - CString(StaticString const& other) noexcept - : CString(other.c_str(), other.length()) {} + // This constructor can be used if the string has NULs in it. + template + CString(StringLiteral const& other, size_t const length) noexcept + : CString(other, length) { + track(true); + } + + CString(StaticString const& other) noexcept // NOLINT(*-explicit-constructor) + : CString(other.c_str(), other.length()) { + track(true); + } CString(const CString& rhs); CString(CString&& rhs) noexcept { + track(true); this->swap(rhs); } - CString& operator=(const CString& rhs); CString& operator=(CString&& rhs) noexcept { @@ -147,7 +162,7 @@ public: return replace(pos, len, str.c_str_safe(), str.size()); } - template >> + template >> CString& replace(size_type pos, size_type len, T str) & noexcept { if (str) { return replace(pos, len, str, strlen(str)); @@ -166,7 +181,7 @@ public: return std::move(*this); } - template >> + template >> CString&& replace(size_type pos, size_type len, T str) && noexcept { this->replace(pos, len, str); return std::move(*this); @@ -188,7 +203,7 @@ public: return replace(pos, 0, str.c_str_safe(), str.size()); } - template >> + template >> CString& insert(size_type pos, T str) & noexcept { if (str) { return replace(pos, 0, str, strlen(str)); @@ -212,7 +227,7 @@ public: return std::move(*this); } - template >> + template >> CString&& insert(size_type pos, T str) && noexcept { this->insert(pos, str); return std::move(*this); @@ -233,7 +248,7 @@ public: return insert(length(), str); } - template>> + template>> CString& append(T str) & noexcept { return insert(length(), str); } @@ -254,7 +269,7 @@ public: return std::move(*this); } - template>> + template>> CString&& append(T str) && noexcept { this->append(str); return std::move(*this); @@ -272,7 +287,7 @@ public: CString& operator+=(const StringLiteral& str) & noexcept { return append(str); } - template >> + template >> CString& operator+=(T str) & noexcept { return append(str); } @@ -332,6 +347,13 @@ public: }; private: + static void do_tracking(bool ctor); + static void track(bool ctor) { + if constexpr (TRACK_AND_LOG_ALLOCATIONS) { + do_tracking(ctor); + } + } + CString& replace(size_type pos, size_type len, char const* str, size_t l) & noexcept; #if !defined(NDEBUG) diff --git a/libs/utils/include/utils/ImmutableCString.h b/libs/utils/include/utils/ImmutableCString.h new file mode 100644 index 0000000000..48fbf43071 --- /dev/null +++ b/libs/utils/include/utils/ImmutableCString.h @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_IMMUTABLECSTRING_H +#define TNT_UTILS_IMMUTABLECSTRING_H + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace utils { + + +class UTILS_PUBLIC ImmutableCString { + static constexpr bool TRACK_AND_LOG_ALLOCATIONS = false; + + template + static constexpr bool is_char_pointer_v = + std::is_pointer_v && std::is_same_v>>; + +public: + using value_type = char; + using size_type = uint32_t; + using difference_type = int32_t; + using const_reference = const value_type&; + using const_pointer = const value_type*; + using const_iterator = const value_type*; + + ImmutableCString() noexcept { + track(true, mIsStatic); + } + + // The string can't have NULs in it. + template + ImmutableCString(const char (&str)[N]) noexcept : mData(str), mSize(N - 1) { // NOLINT(*-explicit-constructor) + track(true, mIsStatic); + } + + // This constructor can be used if the string has NULs in it. + template + ImmutableCString(const char (&str)[N], size_t const length) noexcept + : mData(str), mSize(length) { + track(true, mIsStatic); + } + + template>> + explicit ImmutableCString(T cstr) { + if (cstr) { + initializeFrom(cstr, strlen(cstr)); + } + track(true, mIsStatic); + } + + ImmutableCString(const char* cstr, size_t const length) { + initializeFrom(cstr, length); + track(true, mIsStatic); + } + + ImmutableCString(StaticString const& str) // NOLINT(*-explicit-constructor) + : mData(str.data()), mSize(str.size()) { + track(true, mIsStatic); + } + + ImmutableCString(const ImmutableCString& other) { + if (other.mIsStatic) { + mIsStatic = other.mIsStatic; + mSize = other.mSize; + mData = other.mData; + } else { + initializeFrom(other.mData, other.mSize); + } + track(true, mIsStatic); + } + + ImmutableCString(ImmutableCString&& other) noexcept { + track(true, mIsStatic); + this->swap(other); + } + + ImmutableCString& operator=(const ImmutableCString& other); + + ImmutableCString& operator=(ImmutableCString&& other) noexcept; + + ~ImmutableCString() { + track(false, mIsStatic); + if (!mIsStatic) { + free(const_cast(mData)); + } + } + + bool isStatic() const noexcept { return mIsStatic; } + bool isDynamic() const noexcept { return !mIsStatic; } + + const_pointer c_str_safe() const noexcept { return mData; } + const_pointer c_str() const noexcept { return mData; } + const_pointer data() const noexcept { return mData; } + size_type size() const noexcept { return mSize; } + size_type length() const noexcept { return mSize; } + bool empty() const noexcept { return mSize == 0; } + + const_iterator begin() const noexcept { return mData; } + const_iterator end() const noexcept { return mData + mSize; } + const_iterator cbegin() const noexcept { return begin(); } + const_iterator cend() const noexcept { return end(); } + + const_reference operator[](size_type const pos) const noexcept { + assert(pos < mSize); + return mData[pos]; + } + + const_reference at(size_type const pos) const noexcept { + assert(pos < mSize); + return mData[pos]; + } + + const_reference front() const noexcept { + assert(mSize > 0); + return mData[0]; + } + + const_reference back() const noexcept { + assert(mSize > 0); + return mData[mSize - 1]; + } + + void swap(ImmutableCString& other) noexcept { + std::swap(mData, other.mData); + std::swap(mSize, other.mSize); + std::swap(mIsStatic, other.mIsStatic); + } + +private: + static void do_tracking(bool ctor, bool is_static); + static void track(bool ctor, bool is_static) { + if constexpr (TRACK_AND_LOG_ALLOCATIONS) { + do_tracking(ctor, is_static); + } + } + +#if !defined(NDEBUG) + friend io::ostream& operator<<(io::ostream& out, const ImmutableCString& rhs); +#endif + + void initializeFrom(const char* cstr, size_t length); + + int compare(const ImmutableCString& rhs) const noexcept { + return std::string_view{ mData, mSize }.compare({ rhs.mData, rhs.mSize }); + } + + char const* mData = ""; + uint32_t mSize = 0; + bool mIsStatic = true; + + friend bool operator==(const ImmutableCString& lhs, const ImmutableCString& rhs) noexcept { + return lhs.compare(rhs) == 0; + } + friend bool operator!=(const ImmutableCString& lhs, const ImmutableCString& rhs) noexcept { + return lhs.compare(rhs) != 0; + } + friend bool operator<(const ImmutableCString& lhs, const ImmutableCString& rhs) noexcept { + return lhs.compare(rhs) < 0; + } + friend bool operator>(const ImmutableCString& lhs, const ImmutableCString& rhs) noexcept { + return lhs.compare(rhs) > 0; + } + friend bool operator<=(const ImmutableCString& lhs, const ImmutableCString& rhs) noexcept { + return lhs.compare(rhs) <= 0; + } + friend bool operator>=(const ImmutableCString& lhs, const ImmutableCString& rhs) noexcept { + return lhs.compare(rhs) >= 0; + } +}; + +static_assert(sizeof(ImmutableCString) <= 16, "ImmutableCString should be 16 bytes or less"); + +} // namespace utils + +#endif //TNT_UTILS_IMMUTABLECSTRING_H diff --git a/libs/utils/include/utils/Logger.h b/libs/utils/include/utils/Logger.h index e08decd1a9..b385c85bfa 100644 --- a/libs/utils/include/utils/Logger.h +++ b/libs/utils/include/utils/Logger.h @@ -46,6 +46,7 @@ using absl::LogSeverity; #else #include +#include namespace utils { @@ -67,7 +68,7 @@ public: LogLine& operator=(const LogLine&) = delete; LogLine& operator=(LogLine&&) = delete; - ~LogLine() noexcept { mStream << utils::io::endl; } + ~LogLine() noexcept { mStream << io::endl; } template LogLine& operator<<(T&& value) { diff --git a/libs/utils/include/utils/StaticString.h b/libs/utils/include/utils/StaticString.h index 2cb56723bf..ae2008cbc7 100644 --- a/libs/utils/include/utils/StaticString.h +++ b/libs/utils/include/utils/StaticString.h @@ -19,10 +19,14 @@ #include +#include + #include namespace utils { +class ImmutableCString; + /** * @brief A lightweight string class that stores a pointer to a string literal and its size, without dynamic allocation. * @@ -41,7 +45,7 @@ public: template constexpr StaticString(const char (&str)[M]) noexcept : mString(str, M - 1) {} // NOLINT(*-explicit-constructor) - constexpr StaticString() noexcept = default; + constexpr StaticString() noexcept : mString("", 0) {} constexpr const_pointer c_str() const noexcept { return mString.data(); } constexpr const_pointer data() const noexcept { return mString.data(); } @@ -75,6 +79,10 @@ public: } private: +#if !defined(NDEBUG) + friend io::ostream& operator<<(io::ostream& out, const ImmutableCString& rhs); +#endif + std::string_view mString; friend constexpr bool operator==(const StaticString& lhs, const StaticString& rhs) noexcept { diff --git a/libs/utils/src/CString.cpp b/libs/utils/src/CString.cpp index 34a998cb23..c6bf648e26 100644 --- a/libs/utils/src/CString.cpp +++ b/libs/utils/src/CString.cpp @@ -17,23 +17,49 @@ #include #include +#include #include #include +#include #include #include -#include -#include -#include +#include #include namespace utils { +namespace { +struct CStringStats { + std::atomic_int32_t alive = { 0 }; + std::atomic_int32_t ctor = { 0 }; +}; + +CStringStats gCStringStats{}; +constexpr size_t CSTRING_LOG_INTERVAL = 10000; +} + +void CString::do_tracking(bool ctor) { + if (ctor) { + gCStringStats.ctor.fetch_add(1, std::memory_order_relaxed); + gCStringStats.alive.fetch_add(1, std::memory_order_relaxed); + } else { + gCStringStats.alive.fetch_sub(1, std::memory_order_relaxed); + } + static std::atomic_int32_t sCtorSinceLastLog = { 0 }; + if (UTILS_UNLIKELY(sCtorSinceLastLog.fetch_add(1, std::memory_order_relaxed) == CSTRING_LOG_INTERVAL)) { + LOG(INFO) << "CString stats: " + << gCStringStats.alive.load(std::memory_order_relaxed) << " alive, " + << gCStringStats.ctor.load(std::memory_order_relaxed) << " ctor"; + sCtorSinceLastLog.store(0, std::memory_order_relaxed); + } +} + UTILS_NOINLINE CString::CString(const char* cstr, size_t const length) { + track(true); if (length && cstr) { - Data* const p = static_cast(std::malloc(sizeof(Data) + length + 1)); p->length = size_type(length); mCStr = reinterpret_cast(p + 1); @@ -44,6 +70,7 @@ CString::CString(const char* cstr, size_t const length) { } CString::CString(size_t const length) { + track(true); if (length) { Data* const p = static_cast(std::malloc(sizeof(Data) + length + 1)); p->length = size_type(length); @@ -59,14 +86,13 @@ CString::CString(const CString& rhs) CString& CString::operator=(const CString& rhs) { if (this != &rhs) { - auto *const p = mData ? mData - 1 : nullptr; - new(this) CString(rhs); - std::free(p); + CString(rhs).swap(*this); } return *this; } CString::~CString() noexcept { + track(false); if (mData) { std::free(mData - 1); } @@ -75,6 +101,10 @@ CString::~CString() noexcept { CString& CString::replace(size_type const pos, size_type len, char const* str, size_t const l) & noexcept { assert(pos <= size()); + if (UTILS_UNLIKELY(!l && !len)) { // nothing to do + return *this; + } + len = std::min(len, size() - pos); const size_type newSize = size() - len + l; diff --git a/libs/utils/src/ImmutableCString.cpp b/libs/utils/src/ImmutableCString.cpp new file mode 100644 index 0000000000..2fdc67af99 --- /dev/null +++ b/libs/utils/src/ImmutableCString.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace utils { + +namespace { +struct ImmutableCStringStats { + std::atomic_int32_t alive = { 0 }; + std::atomic_int32_t ctor = { 0 }; + std::atomic_int32_t staticStrings = { 0 }; + std::atomic_int32_t heapStrings = { 0 }; +}; + +ImmutableCStringStats gImmutableCStringStats{}; +constexpr size_t IMMUTABLECSTRING_LOG_INTERVAL = 10000; +} + +void ImmutableCString::do_tracking(bool ctor, bool is_static) { + if (ctor) { + gImmutableCStringStats.ctor.fetch_add(1, std::memory_order_relaxed); + gImmutableCStringStats.alive.fetch_add(1, std::memory_order_relaxed); + if (is_static) { + gImmutableCStringStats.staticStrings.fetch_add(1, std::memory_order_relaxed); + } else { + gImmutableCStringStats.heapStrings.fetch_add(1, std::memory_order_relaxed); + } + } else { + gImmutableCStringStats.alive.fetch_sub(1, std::memory_order_relaxed); + if (is_static) { + gImmutableCStringStats.staticStrings.fetch_sub(1, std::memory_order_relaxed); + } else { + gImmutableCStringStats.heapStrings.fetch_sub(1, std::memory_order_relaxed); + } + } + static std::atomic_int32_t sCtorSinceLastLog = { 0 }; + if (UTILS_UNLIKELY(sCtorSinceLastLog.fetch_add(1, std::memory_order_relaxed) == IMMUTABLECSTRING_LOG_INTERVAL)) { + LOG(INFO) << "ImmutableCString stats: " + << gImmutableCStringStats.alive.load(std::memory_order_relaxed) << " alive, " + << gImmutableCStringStats.ctor.load(std::memory_order_relaxed) << " ctor, " + << gImmutableCStringStats.staticStrings.load(std::memory_order_relaxed) << " static, " + << gImmutableCStringStats.heapStrings.load(std::memory_order_relaxed) << " heap"; + sCtorSinceLastLog.store(0, std::memory_order_relaxed); + } +} + +ImmutableCString& ImmutableCString::operator=(const ImmutableCString& other) { + if (this != &other) { + ImmutableCString(other).swap(*this); + } + return *this; +} + +ImmutableCString& ImmutableCString::operator=(ImmutableCString&& other) noexcept { + this->swap(other); + return *this; +} + + +void ImmutableCString::initializeFrom(const char* cstr, size_t const length) { + if (length > 0 && cstr) { + char* buffer = static_cast(malloc(length + 1)); + if (UTILS_LIKELY(buffer)) { + memcpy(buffer, cstr, length); + buffer[length] = '\0'; + mData = buffer; + mSize = length; + mIsStatic = false; + return; + } + } + mData = ""; + mSize = 0; + mIsStatic = true; +} + +#if !defined(NDEBUG) +io::ostream& operator<<(io::ostream& out, const ImmutableCString& rhs) { + return out << rhs.c_str(); +} + +io::ostream& operator<<(io::ostream& out, const StaticString& rhs) { + return out << rhs.c_str(); +} +#endif + +} // namespace utils diff --git a/libs/utils/test/test_ImmutableCString.cpp b/libs/utils/test/test_ImmutableCString.cpp new file mode 100644 index 0000000000..a80fb179e3 --- /dev/null +++ b/libs/utils/test/test_ImmutableCString.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include +#include +#include +#include + +using namespace utils; + +TEST(ImmutableCString, EmptyString) { + ImmutableCString const emptyString(""); + EXPECT_STREQ("", emptyString.c_str()); + EXPECT_EQ(0, emptyString.length()); + EXPECT_TRUE(emptyString.empty()); + EXPECT_TRUE(emptyString.isStatic()); +} + +TEST(ImmutableCString, Constructors) { + // ImmutableCString() + { + ImmutableCString const str; + EXPECT_STREQ("", str.c_str()); + EXPECT_EQ(0, str.length()); + EXPECT_TRUE(str.empty()); + EXPECT_TRUE(str.isStatic()); + } + // ImmutableCString(const char* cstr, size_t length) + { + ImmutableCString const str("foobar", 3); + EXPECT_STREQ("foo", str.c_str()); + EXPECT_EQ(3, str.length()); + EXPECT_TRUE(str.isDynamic()); + } + // ImmutableCString(const char* cstr) + { + const char* hello_cstr = "hello"; + ImmutableCString const str(hello_cstr); + EXPECT_STREQ("hello", str.c_str()); + EXPECT_EQ(5, str.length()); + EXPECT_TRUE(str.isDynamic()); + } + // ImmutableCString(StringLiteral) + { + ImmutableCString const str("literal"); // this uses the template constructor + EXPECT_STREQ("literal", str.c_str()); + EXPECT_EQ(7, str.length()); + EXPECT_TRUE(str.isStatic()); + } + // Copy constructor + { + ImmutableCString const s1("copy me"); + EXPECT_TRUE(s1.isStatic()); + ImmutableCString const s2(s1); // NOLINT(*-unnecessary-copy-initialization) + EXPECT_STREQ("copy me", s2.c_str()); + EXPECT_TRUE(s2.isStatic()); + } + // Move constructor + { + ImmutableCString s1("move me"); + EXPECT_TRUE(s1.isStatic()); + ImmutableCString const s2(std::move(s1)); + EXPECT_STREQ("move me", s2.c_str()); + EXPECT_TRUE(s2.isStatic()); + } +} + +TEST(ImmutableCString, Assignment) { + // Copy assignment + { + ImmutableCString const s1("copy"); + ImmutableCString s2; + s2 = s1; + EXPECT_STREQ("copy", s2.c_str()); + EXPECT_TRUE(s2.isStatic()); + } + // Move assignment + { + ImmutableCString s1("move"); + ImmutableCString s2; + s2 = std::move(s1); + EXPECT_STREQ("move", s2.c_str()); + EXPECT_TRUE(s2.isStatic()); + } + // self-copy-assignment + { + ImmutableCString s1("self"); + // This looks strange, but it's an important edge case to test for assignment operators. + s1 = s1; + EXPECT_STREQ("self", s1.c_str()); + EXPECT_TRUE(s1.isStatic()); + } + // self-move-assignment + { + ImmutableCString s1("self-move"); + s1 = std::move(s1); + // A self-move-assignment should leave the object in a valid state. + // Our implementation has a guard against self-move, so the object is unchanged. + EXPECT_STREQ("self-move", s1.c_str()); + EXPECT_TRUE(s1.isStatic()); + } +} + +TEST(ImmutableCString, Swap) { + ImmutableCString s1("first"); + ImmutableCString s2("second"); + + size_t const l1 = s1.length(); + size_t const l2 = s2.length(); + + s1.swap(s2); + + EXPECT_STREQ("second", s1.c_str()); + EXPECT_STREQ("first", s2.c_str()); + EXPECT_EQ(l2, s1.length()); + EXPECT_EQ(l1, s2.length()); +} + +TEST(ImmutableCString, Comparison) { + ImmutableCString const s1("abc"); + ImmutableCString const s2("abc"); + ImmutableCString const s3("def"); + ImmutableCString const s4("ab"); + + EXPECT_TRUE(s1 == s2); + EXPECT_FALSE(s1 == s3); + + EXPECT_TRUE(s1 != s3); + EXPECT_FALSE(s1 != s2); + + EXPECT_TRUE(s1 < s3); + EXPECT_FALSE(s3 < s1); + + EXPECT_TRUE(s3 > s1); + EXPECT_FALSE(s1 > s3); + + EXPECT_TRUE(s1 <= s2); + EXPECT_TRUE(s1 <= s3); + EXPECT_FALSE(s3 <= s1); + + EXPECT_TRUE(s2 >= s1); + EXPECT_TRUE(s3 >= s1); + EXPECT_FALSE(s1 >= s3); + + EXPECT_TRUE(s4 < s1); + EXPECT_TRUE(s1 > s4); +} + +TEST(ImmutableCString, ElementAccess) { + // Test with statically-allocated string + const ImmutableCString cstr("const"); + EXPECT_EQ('c', cstr.front()); + EXPECT_EQ('t', cstr.back()); + EXPECT_EQ('n', cstr[2]); + EXPECT_EQ('s', cstr.at(3)); + + // Test with heap-allocated string + std::string const a_normal_string = "a normal string"; + const ImmutableCString v(a_normal_string.c_str()); // heap allocation + EXPECT_EQ('a', v.front()); + EXPECT_EQ('g', v.back()); + EXPECT_EQ(' ', v[1]); + EXPECT_EQ('n', v.at(2)); + + // iterators + std::string const s(cstr.begin(), cstr.end()); + EXPECT_EQ("const", s); + + EXPECT_TRUE(std::equal(cstr.begin(), cstr.end(), "const")); +} + +TEST(ImmutableCString, NoHeapAllocation) { + ImmutableCString s("hello world"); // no allocation + EXPECT_TRUE(s.isStatic()); + ImmutableCString t(s); // no allocation + EXPECT_TRUE(t.isStatic()); + s = t; // no allocation + EXPECT_TRUE(s.isStatic()); + t = std::move(s); // no allocation and s is now empty + EXPECT_TRUE(t.isStatic()); + EXPECT_STREQ("hello world", t.c_str()); +} + +TEST(ImmutableCString, HeapAllocation) { + std::string const a_normal_string = "a normal string"; + ImmutableCString const v(a_normal_string.c_str()); // heap allocation + EXPECT_TRUE(v.isDynamic()); + EXPECT_STREQ("a normal string", v.c_str()); +}