From 2a72e8aee82c9b3f56a6114ab3af3d71233051f9 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 6 Mar 2019 15:39:23 -0800 Subject: [PATCH] break dependency of Program on SamplerInterfaceBlock and SamplerBindingMap Instead of passing SamplerInterfaceBlocks and a SamplerBindingMap to Program, we now set a 'sampler group' per binding point: Program::addSamplerGroup(...) A sampler group here consists of a list of N 'Sampler' and a 'Sampler' is just a unique name (unifrom name in the shader) and binding point in the shader. That's all the driver layer needs. With this change we get rid of the code that re-created the uniform names in the driver -- this should never have been done there. And we also remove the hash-map lookups in vulkan and metal drivers. --- filament/src/Engine.cpp | 24 +++++- filament/src/Material.cpp | 25 +++++- filament/src/details/Material.h | 1 + filament/src/driver/Program.cpp | 16 ++-- filament/src/driver/Program.h | 37 ++++----- filament/src/driver/metal/MetalDriver.mm | 5 +- filament/src/driver/metal/MetalHandles.h | 2 +- filament/src/driver/metal/MetalHandles.mm | 2 +- filament/src/driver/opengl/OpenGLProgram.cpp | 80 +++++-------------- filament/src/driver/vulkan/VulkanDriver.cpp | 7 +- filament/src/driver/vulkan/VulkanHandles.cpp | 15 ++-- filament/src/driver/vulkan/VulkanHandles.h | 2 +- .../private/filament/SamplerInterfaceBlock.h | 2 + libs/filabridge/src/SamplerInterfaceBlock.cpp | 20 +++++ libs/filamat/src/shaders/CodeGenerator.cpp | 25 +++--- libs/utils/include/utils/CString.h | 2 +- libs/utils/src/CString.cpp | 4 +- 17 files changed, 142 insertions(+), 127 deletions(-) diff --git a/filament/src/Engine.cpp b/filament/src/Engine.cpp index ac6d668bdd..26cc83156c 100644 --- a/filament/src/Engine.cpp +++ b/filament/src/Engine.cpp @@ -449,12 +449,30 @@ Handle FEngine::createPostProcessProgram(MaterialParser& parser, Program pb; pb .diagnostics(CString("Post Process")) - .withSamplerBindings(pBindings) .withVertexShader(vShaderBuilder.data(), vShaderBuilder.size()) .withFragmentShader(fShaderBuilder.data(), fShaderBuilder.size()) .addUniformBlock(BindingPoints::PER_VIEW, &PerViewUib::getUib()) - .addUniformBlock(BindingPoints::POST_PROCESS, &PostProcessingUib::getUib()) - .addSamplerBlock(BindingPoints::POST_PROCESS, &SibGenerator::getPostProcessSib()); + .addUniformBlock(BindingPoints::POST_PROCESS, &PostProcessingUib::getUib()); + + auto addSamplerGroup = [&pb] + (uint8_t bindingPoint, SamplerInterfaceBlock const& sib, SamplerBindingMap const& map) { + if (sib.getSize()) { + std::vector samplers; + auto const& list = sib.getSamplerInfoList(); + for (size_t i = 0, c = sib.getSize(); i < c; ++i) { + CString uniformName( + SamplerInterfaceBlock::getUniformName(sib.getName().c_str(), + list[i].name.c_str())); + uint8_t binding, group; + map.getSamplerBinding(bindingPoint, (uint8_t)i, &binding, &group); + samplers.push_back({ uniformName, binding }); + } + pb.addSamplerGroup(bindingPoint, samplers.data(), samplers.size()); + } + }; + + addSamplerGroup(BindingPoints::POST_PROCESS, SibGenerator::getPostProcessSib(), *pBindings); + auto program = const_cast(mCommandStream).createProgram(std::move(pb)); assert(program); return program; diff --git a/filament/src/Material.cpp b/filament/src/Material.cpp index 2bcaafc64d..f0393ca899 100644 --- a/filament/src/Material.cpp +++ b/filament/src/Material.cpp @@ -318,13 +318,30 @@ Handle FMaterial::getProgramSlow(uint8_t variantKey) const noexcept { pb .diagnostics(mName, variantKey) .withVertexShader(vsBuilder.data(), vsBuilder.size()) .withFragmentShader(fsBuilder.data(), fsBuilder.size()) - .withSamplerBindings(&mSamplerBindings) .addUniformBlock(BindingPoints::PER_VIEW, &UibGenerator::getPerViewUib()) .addUniformBlock(BindingPoints::LIGHTS, &UibGenerator::getLightsUib()) .addUniformBlock(BindingPoints::PER_RENDERABLE, &UibGenerator::getPerRenderableUib()) - .addUniformBlock(BindingPoints::PER_MATERIAL_INSTANCE, &mUniformInterfaceBlock) - .addSamplerBlock(BindingPoints::PER_VIEW, &SibGenerator::getPerViewSib()) - .addSamplerBlock(BindingPoints::PER_MATERIAL_INSTANCE, &mSamplerInterfaceBlock); + .addUniformBlock(BindingPoints::PER_MATERIAL_INSTANCE, &mUniformInterfaceBlock); + + auto addSamplerGroup = [&pb] + (uint8_t bindingPoint, SamplerInterfaceBlock const& sib, SamplerBindingMap const& map) { + if (sib.getSize()) { + std::vector samplers; + auto const& list = sib.getSamplerInfoList(); + for (size_t i = 0, c = sib.getSize(); i < c; ++i) { + CString uniformName( + SamplerInterfaceBlock::getUniformName(sib.getName().c_str(), + list[i].name.c_str())); + uint8_t binding, group; + map.getSamplerBinding(bindingPoint, (uint8_t)i, &binding, &group); + samplers.push_back({ uniformName, binding }); + } + pb.addSamplerGroup(bindingPoint, samplers.data(), samplers.size()); + } + }; + + addSamplerGroup(BindingPoints::PER_VIEW, SibGenerator::getPerViewSib(), mSamplerBindings); + addSamplerGroup(BindingPoints::PER_MATERIAL_INSTANCE, mSamplerInterfaceBlock, mSamplerBindings); if (Variant(variantKey).hasSkinning()) { pb.addUniformBlock(BindingPoints::PER_RENDERABLE_BONES, &UibGenerator::getPerRenderableBonesUib()); diff --git a/filament/src/details/Material.h b/filament/src/details/Material.h index 6355ff72fb..bb02eaa944 100644 --- a/filament/src/details/Material.h +++ b/filament/src/details/Material.h @@ -23,6 +23,7 @@ #include +#include #include #include diff --git a/filament/src/driver/Program.cpp b/filament/src/driver/Program.cpp index 64ef025fb6..57df38c4d6 100644 --- a/filament/src/driver/Program.cpp +++ b/filament/src/driver/Program.cpp @@ -15,6 +15,8 @@ */ #include "driver/Program.h" +#include "Program.h" + #include #include @@ -42,11 +44,6 @@ Program& Program::diagnostics(utils::CString&& name, uint8_t variant) noexcept { return *this; } -Program& Program::withSamplerBindings(const SamplerBindingMap* bindings) { - mSamplerBindings = bindings; - return *this; -} - Program& Program::shader(Program::Shader shader, void const* data, size_t size) noexcept { std::vector blob(size); std::copy_n((const uint8_t *)data, size, blob.data()); @@ -59,12 +56,15 @@ Program& Program::addUniformBlock(size_t index, const UniformInterfaceBlock* ib) return *this; } -Program& Program::addSamplerBlock(size_t index, const SamplerInterfaceBlock* sb) { - mSamplerInterfaceBlocks[index] = sb; - mSamplerCount++; +Program& Program::addSamplerGroup(size_t bindingPoint, const Program::Sampler* samplers, size_t count) { + auto& samplerList = mSamplerGroups[bindingPoint]; + samplerList.clear(); + samplerList.insert(samplerList.begin(), samplers, samplers + count); + mHasSamplers = true; return *this; } + #if !defined(NDEBUG) io::ostream& operator<<(io::ostream& out, const Program& builder) { // FIXME: maybe do better here! diff --git a/filament/src/driver/Program.h b/filament/src/driver/Program.h index 6b1e2703b3..9e15772089 100644 --- a/filament/src/driver/Program.h +++ b/filament/src/driver/Program.h @@ -18,7 +18,6 @@ #define TNT_FILAMENT_DRIVER_PROGRAM_H #include -#include #include #include @@ -29,7 +28,6 @@ namespace filament { -class SamplerInterfaceBlock; class UniformInterfaceBlock; class Program { @@ -44,6 +42,13 @@ public: FRAGMENT = 1 }; + struct Sampler { + utils::CString name = {}; // name of the sampler in the shader + size_t binding = 0; // binding point of the sampler in the shader + }; + + using SamplerGroupInfo = std::array, NUM_SAMPLER_BINDINGS>; + Program() noexcept; Program(const Program& rhs) = delete; Program& operator=(const Program& rhs) = delete; @@ -62,13 +67,10 @@ public: // The lifetime of UniformInterfaceBlock* must be longer than Program's Program& addUniformBlock(size_t index, const UniformInterfaceBlock* ib); - // sets a sampler interface block for this program - // The lifetime of SamplerInterfaceBlock* must be longer than Program's - Program& addSamplerBlock(size_t index, const SamplerInterfaceBlock* ub); - - // sets up sampler bindings for this program - // The lifetime of SamplerBindingMap* must be longer than Program's - Program& withSamplerBindings(const SamplerBindingMap* bindings); + // sets the 'index' sampler group descriptor for this program. + // 'samplers' can be destroyed after this call. + // This effectively associates a set of (BindingPoints, index) to a (shader-binding) + Program& addSamplerGroup(size_t bindingPoint, Sampler const* samplers, size_t count); Program& withVertexShader(void const* data, size_t size) { return shader(Shader::VERTEX, data, size); @@ -87,13 +89,8 @@ public: return mUniformInterfaceBlocks; } - std::array const& - getSamplerInterfaceBlocks() const noexcept { - return mSamplerInterfaceBlocks; - } - - const SamplerBindingMap* getSamplerBindings() const noexcept { - return mSamplerBindings; + SamplerGroupInfo const& getSamplerGroupInfo() const { + return mSamplerGroups; } const utils::CString& getName() const noexcept { @@ -105,7 +102,7 @@ public: } bool hasSamplers() const noexcept { - return mSamplerCount > 0; + return mHasSamplers; } private: @@ -116,11 +113,11 @@ private: // FIXME: none of these fields should be public as this is a public API std::array mUniformInterfaceBlocks = {}; - std::array mSamplerInterfaceBlocks = {}; - const SamplerBindingMap* mSamplerBindings = nullptr; + SamplerGroupInfo mSamplerGroups = {}; + std::array, NUM_SHADER_TYPES> mShadersSource; - size_t mSamplerCount = 0; utils::CString mName; + bool mHasSamplers = false; uint8_t mVariant; }; diff --git a/filament/src/driver/metal/MetalDriver.mm b/filament/src/driver/metal/MetalDriver.mm index 71e974afdd..17da389ea6 100644 --- a/filament/src/driver/metal/MetalDriver.mm +++ b/filament/src/driver/metal/MetalDriver.mm @@ -757,10 +757,9 @@ void MetalDriver::enumerateSamplerGroups( if (!sampler->t) { continue; } - uint8_t binding, group; - if (program->samplerBindings.getSamplerBinding(bufferIdx, samplerIdx, &binding, - &group)) { + if (!program->samplerBindings[bufferIdx].empty()) { + uint8_t binding = (uint8_t)program->samplerBindings[bufferIdx][samplerIdx].binding; f(sampler, binding); } } diff --git a/filament/src/driver/metal/MetalHandles.h b/filament/src/driver/metal/MetalHandles.h index 69dad8882e..b24fa817ba 100644 --- a/filament/src/driver/metal/MetalHandles.h +++ b/filament/src/driver/metal/MetalHandles.h @@ -97,7 +97,7 @@ struct MetalProgram : public HwProgram { id vertexFunction; id fragmentFunction; - SamplerBindingMap samplerBindings; + Program::SamplerGroupInfo samplerBindings; }; struct MetalTexture : public HwTexture { diff --git a/filament/src/driver/metal/MetalHandles.mm b/filament/src/driver/metal/MetalHandles.mm index da932984e3..4671d62fba 100644 --- a/filament/src/driver/metal/MetalHandles.mm +++ b/filament/src/driver/metal/MetalHandles.mm @@ -222,7 +222,7 @@ MetalProgram::MetalProgram(id device, const Program& program) noexcep [library release]; } - samplerBindings = *program.getSamplerBindings(); + samplerBindings = program.getSamplerGroupInfo(); } MetalProgram::~MetalProgram() { diff --git a/filament/src/driver/opengl/OpenGLProgram.cpp b/filament/src/driver/opengl/OpenGLProgram.cpp index ace15b965a..fa8beb80f3 100644 --- a/filament/src/driver/opengl/OpenGLProgram.cpp +++ b/filament/src/driver/opengl/OpenGLProgram.cpp @@ -32,17 +32,6 @@ namespace filament { using namespace filament::math; using namespace utils; -// we just want a small, tight loop for our purpose here, so we avoid std::copy_n -UTILS_NOINLINE -static char* copy_n(char const* s, size_t n, char* d) noexcept { - char* const e = d + n; -#pragma nounroll - while (d != e) { - *d++ = *s++; - } - return d; -} - OpenGLProgram::OpenGLProgram(OpenGLDriver* gl, const Program& programBuilder) noexcept : HwProgram(programBuilder.getName()), mIsValid(false) { @@ -127,61 +116,36 @@ OpenGLProgram::OpenGLProgram(OpenGLDriver* gl, const Program& programBuilder) no // activate this program so we can set all its samplers once and for all (glUniform1i) gl->useProgram(program); - auto const& samplerInterfaceBlocks = programBuilder.getSamplerInterfaceBlocks(); + auto const& samplerGroupInfo = programBuilder.getSamplerGroupInfo(); auto& indicesRun = mIndicesRuns; uint8_t numUsedBindings = 0; uint8_t tmu = 0; - char uniformName[256]; - #pragma nounroll - for (size_t i = 0, c = samplerInterfaceBlocks.size(); i < c; i++) { - auto const& sib = samplerInterfaceBlocks[i]; - if (sib != nullptr) { + for (size_t i = 0, c = samplerGroupInfo.size(); i < c; i++) { + auto const& groupInfo = samplerGroupInfo[i]; + if (!groupInfo.empty()) { // Cache the sampler uniform locations for each interface block - auto const& infos(sib->getSamplerInfoList()); - if (!infos.empty()) { - BlockInfo& info = mBlockInfos[numUsedBindings]; - info.binding = uint8_t(i); - - // sampler interface block name - CString const& sibName = sib->getName(); - char* const prefix = copy_n(sibName.begin(), - std::min(sizeof(uniformName) / 2, (size_t)sibName.size()), - uniformName); - if (uniformName[0] >= 'A' && uniformName[0] <= 'Z') { - uniformName[0] |= 0x20; // poor man's tolower() - } - *prefix = '_'; - - uint8_t count = 0; - for (uint8_t j = 0, m = uint8_t(infos.size()); j < m; ++j) { - // build unique name for this uniform (sampler) - auto const& e = infos[j]; - char* last = copy_n(e.name.begin(), - std::min(sizeof(uniformName) / 2 - 2, (size_t)e.name.size()), - prefix + 1); - *last++ = 0; // null terminator - assert(last <= std::end(uniformName)); - - // find its location and associate a TMU to it - GLint loc = glGetUniformLocation(program, uniformName); - if (loc >= 0) { - glUniform1i(loc, tmu); - indicesRun[tmu] = j; - count++; - tmu++; - } else { - // glGetUniformLocation could fail if the uniform is not used - // in the program. We should just ignore the error in that case. - } - } - - if (count > 0) { - numUsedBindings++; - info.count = uint8_t(count - 1); + BlockInfo& info = mBlockInfos[numUsedBindings]; + info.binding = uint8_t(i); + uint8_t count = 0; + for (uint8_t j = 0, m = uint8_t(groupInfo.size()); j < m; ++j) { + // find its location and associate a TMU to it + GLint loc = glGetUniformLocation(program, groupInfo[j].name.c_str()); + if (loc >= 0) { + glUniform1i(loc, tmu); + indicesRun[tmu] = j; + count++; + tmu++; + } else { + // glGetUniformLocation could fail if the uniform is not used + // in the program. We should just ignore the error in that case. } } + if (count > 0) { + numUsedBindings++; + info.count = uint8_t(count - 1); + } } } mUsedBindingsCount = numUsedBindings; diff --git a/filament/src/driver/vulkan/VulkanDriver.cpp b/filament/src/driver/vulkan/VulkanDriver.cpp index b4eadd872c..7695885b04 100644 --- a/filament/src/driver/vulkan/VulkanDriver.cpp +++ b/filament/src/driver/vulkan/VulkanDriver.cpp @@ -963,9 +963,10 @@ void VulkanDriver::draw(Driver::PipelineState pipelineState, Driver::RenderPrimi // Obtain the global sampler binding index and pass this to VulkanBinder. Note that // "binding" is an offset that is global to the shader, whereas "samplerIndex" is an // offset into the virtual sampler buffer. - uint8_t binding, group; - if (program->samplerBindings.getSamplerBinding(bufferIdx, samplerIndex, &binding, - &group)) { + uint8_t binding; + + if (!program->samplerBindings[bufferIdx].empty()) { + binding = (uint8_t)program->samplerBindings[bufferIdx][samplerIndex].binding; const SamplerParams& samplerParams = sampler->s; VkSampler vksampler = mSamplerCache.getSampler(samplerParams); const auto* tex = handle_const_cast(mHandleMap, sampler->t); diff --git a/filament/src/driver/vulkan/VulkanHandles.cpp b/filament/src/driver/vulkan/VulkanHandles.cpp index bfd6e84528..3fe1b37c93 100644 --- a/filament/src/driver/vulkan/VulkanHandles.cpp +++ b/filament/src/driver/vulkan/VulkanHandles.cpp @@ -73,21 +73,16 @@ VulkanProgram::VulkanProgram(VulkanContext& context, const Program& builder) noe return; } - // Make a copy of the binding map that lives in filament::Material. - const SamplerBindingMap* pSamplerBindings = builder.getSamplerBindings(); - if (pSamplerBindings) { - #if FILAMENT_VULKAN_VERBOSE - utils::slog.d << "Created VulkanProgram " << builder.getName().c_str() + // Make a copy of the binding map + samplerBindings = builder.getSamplerGroupInfo(); +#if FILAMENT_VULKAN_VERBOSE + utils::slog.d << "Created VulkanProgram " << builder.getName().c_str() << ", variants = (0x" << utils::io::hex << (builder.getVariant() & filament::Variant::VERTEX_MASK) << ", 0x" << (builder.getVariant() & filament::Variant::FRAGMENT_MASK) << "), " << "shaders = (" << bundle.vertex << ", " << bundle.fragment << ")" << utils::io::endl; - #endif - samplerBindings = *pSamplerBindings; - return; - } - utils::slog.w << "Missing sampler bindings: " << builder.getName().c_str() << utils::io::endl; +#endif } VulkanProgram::~VulkanProgram() { diff --git a/filament/src/driver/vulkan/VulkanHandles.h b/filament/src/driver/vulkan/VulkanHandles.h index ea2aa38682..e8975b88f4 100644 --- a/filament/src/driver/vulkan/VulkanHandles.h +++ b/filament/src/driver/vulkan/VulkanHandles.h @@ -32,7 +32,7 @@ struct VulkanProgram : public HwProgram { ~VulkanProgram(); VulkanContext& context; VulkanBinder::ProgramBundle bundle; - SamplerBindingMap samplerBindings; + Program::SamplerGroupInfo samplerBindings; }; struct VulkanTexture; diff --git a/libs/filabridge/include/private/filament/SamplerInterfaceBlock.h b/libs/filabridge/include/private/filament/SamplerInterfaceBlock.h index 8da209fc53..5da62f8ebd 100644 --- a/libs/filabridge/include/private/filament/SamplerInterfaceBlock.h +++ b/libs/filabridge/include/private/filament/SamplerInterfaceBlock.h @@ -130,6 +130,8 @@ public: bool isEmpty() const noexcept { return mSamplersInfoList.empty(); } + static utils::CString getUniformName(const char* group, const char* sampler) noexcept; + private: friend class Builder; diff --git a/libs/filabridge/src/SamplerInterfaceBlock.cpp b/libs/filabridge/src/SamplerInterfaceBlock.cpp index 669e3e3af3..0befe381f0 100644 --- a/libs/filabridge/src/SamplerInterfaceBlock.cpp +++ b/libs/filabridge/src/SamplerInterfaceBlock.cpp @@ -113,4 +113,24 @@ const SamplerInterfaceBlock::SamplerInfo* SamplerInterfaceBlock::getSamplerInfo( return &mSamplersInfoList[pos->second]; } +utils::CString SamplerInterfaceBlock::getUniformName(const char* group, const char* sampler) noexcept { + char uniformName[256]; + + // sampler interface block name + char* const prefix = std::copy_n(group, + std::min(sizeof(uniformName) / 2, strlen(group)), uniformName); + if (uniformName[0] >= 'A' && uniformName[0] <= 'Z') { + uniformName[0] |= 0x20; // poor man's tolower() + } + *prefix = '_'; + + char* last = std::copy_n(sampler, + std::min(sizeof(uniformName) / 2 - 2, strlen(sampler)), + prefix + 1); + *last++ = 0; // null terminator + assert(last <= std::end(uniformName)); + + return CString{ uniformName, size_t(last - uniformName) - 1u }; +} + } // namespace filament diff --git a/libs/filamat/src/shaders/CodeGenerator.cpp b/libs/filamat/src/shaders/CodeGenerator.cpp index cec2d5950f..bdae48e9a2 100644 --- a/libs/filamat/src/shaders/CodeGenerator.cpp +++ b/libs/filamat/src/shaders/CodeGenerator.cpp @@ -280,11 +280,12 @@ std::ostream& CodeGenerator::generateSamplers( return out; } - const CString& blockName = sib.getName(); - std::string instanceName(blockName.c_str()); - instanceName.front() = char(std::tolower((unsigned char) instanceName.front())); - for (auto const& info : infos) { + + CString uniformName = + SamplerInterfaceBlock::getUniformName( + sib.getName().c_str(), info.name.c_str()); + auto type = info.type; if (type == SamplerType::SAMPLER_EXTERNAL && mShaderModel != ShaderModel::GL_ES_30) { // we're generating the shader for the desktop, where we assume external textures @@ -297,8 +298,7 @@ std::ostream& CodeGenerator::generateSamplers( const uint32_t bindingIndex = (uint32_t) firstBinding + info.offset; out << "layout(binding = " << bindingIndex << ") "; } - out << "uniform " << precision << " " << typeName << " " << - instanceName << "_" << info.name.c_str(); + out << "uniform " << precision << " " << typeName << " " << uniformName.c_str(); out << ";\n"; } out << "\n"; @@ -313,23 +313,24 @@ void CodeGenerator::fixupExternalSamplers( return; } - const CString& blockName = sib.getName(); - std::string instanceName(blockName.c_str()); - instanceName.front() = char(std::tolower((unsigned char) instanceName.front())); - bool hasExternalSampler = false; // Replace sampler2D declarations by samplerExternal declarations as they may have // been swapped during a previous optimization step for (auto const& info : infos) { if (info.type == SamplerType::SAMPLER_EXTERNAL) { - auto name = std::string("sampler2D ") + instanceName + '_' + info.name.c_str(); + + CString uniformName = + SamplerInterfaceBlock::getUniformName( + sib.getName().c_str(), info.name.c_str()); + + auto name = std::string("sampler2D ") + uniformName.c_str(); size_t index = shader.find(name); if (index != std::string::npos) { hasExternalSampler = true; auto newName = - std::string("samplerExternalOES ") + instanceName + '_' + info.name.c_str(); + std::string("samplerExternalOES ") + uniformName.c_str(); shader.replace(index, name.size(), newName); } } diff --git a/libs/utils/include/utils/CString.h b/libs/utils/include/utils/CString.h index fe06a0b0c5..526d7d7945 100644 --- a/libs/utils/include/utils/CString.h +++ b/libs/utils/include/utils/CString.h @@ -201,7 +201,7 @@ public: CString() noexcept = default; // cstr must be a null terminated string and length == strlen(cstr) - CString(const char* cstr, size_type length); + CString(const char* cstr, size_t length); template explicit CString(StringLiteral const& other) noexcept // NOLINT(google-explicit-constructor) diff --git a/libs/utils/src/CString.cpp b/libs/utils/src/CString.cpp index 789b21f00c..017a09d2bc 100644 --- a/libs/utils/src/CString.cpp +++ b/libs/utils/src/CString.cpp @@ -33,11 +33,11 @@ int StaticString::compare(const StaticString& rhs) const noexcept { // ------------------------------------------------------------------------------------------------ UTILS_NOINLINE -CString::CString(const char* cstr, size_type length) { +CString::CString(const char* cstr, size_t length) { if (length && cstr) { assert(length == strlen(cstr)); Data* p = (Data*)malloc(sizeof(Data) + length + 1); - p->length = length; + p->length = (size_type)length; mCStr = (value_type*)(p + 1); // we don't use memcpy here to avoid a call to libc, the generated code is pretty good. std::uninitialized_copy_n(cstr, length + 1, mCStr);