Skip to content

Commit db2cb49

Browse files
mosraHugo Amiard
and
Hugo Amiard
committed
GL: port Shader and ShaderProgram away from std::string.
Same as in the previous commit, most cases are inputs so a StringStl.h compatibility include will do, the only breaking change is GL::Shader::sources() which now returns a StringIterable instead of a std::vector<std::string> (ew). Awesome about this whole thing is that The Shader API now allows creating a shader from sources coming either from string view literals or Utility::Resource completely without having to allocate any strings internally, because all those can be just non-owning references wrapped with String::nullTerminatedGlobalView(). The only parts which aren't references are the #line markers, but (especially on 64bit) those can easily fit into the 22-byte (or 10-byte on 32bit) SSO storage. Also, various Shader constructors and assignment operators had to be deinlined in order to avoid having to include the String header, which would be needed for Array destruction during a move. Co-authored-by: Hugo Amiard <[email protected]>
1 parent 2f57162 commit db2cb49

29 files changed

+607
-395
lines changed

Diff for: doc/changelog.dox

+6
Original file line numberDiff line numberDiff line change
@@ -1137,6 +1137,12 @@ See also:
11371137
- As part of the ongoing STL header dependency cleanup, the following
11381138
breaking changes related to @ref std::string and a @ref std::vector are
11391139
done:
1140+
- @ref GL::AbstractShaderProgram::validate() now returns a
1141+
@relativeref{Corrade,Containers::String} instead of a @ref std::string;
1142+
@ref GL::Shader::sources() now returns a
1143+
@relativeref{Corrade,Containers::StringIterable} instead of a
1144+
@ref std::vector of a @ref std::string See also [mosra/magnum#499](https://github.com/mosra/magnum/pull/499)
1145+
and [mosra/magnum#608](https://github.com/mosra/magnum/pull/608).
11401146
- @ref GL::Context::vendorString(),
11411147
@relativeref{GL::Context,rendererString()},
11421148
@relativeref{GL::Context,versionString()},

Diff for: doc/snippets/MagnumGL.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
#include <Corrade/Containers/ArrayViewStl.h>
2828
#include <Corrade/Containers/Iterable.h>
2929
#include <Corrade/Containers/Reference.h>
30+
#include <Corrade/Containers/StringIterable.h>
31+
#include <Corrade/Containers/StringView.h>
3032
#include <Corrade/Containers/Triple.h>
3133
#include <Corrade/TestSuite/Tester.h>
3234

Diff for: doc/snippets/MagnumShaders-gl.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
#include <Corrade/Containers/ArrayViewStl.h>
2929
#include <Corrade/Containers/Iterable.h>
3030
#include <Corrade/Containers/Pair.h>
31-
#include <Corrade/Utility/FormatStl.h>
31+
#include <Corrade/Containers/String.h>
32+
#include <Corrade/Utility/Format.h>
3233

3334
#include "Magnum/ImageView.h"
3435
#include "Magnum/PixelFormat.h"
@@ -683,7 +684,7 @@ bindAttributeLocation(Shaders::GenericGL3D::Normal::Location, "normal");
683684
{
684685
GL::Shader vert{GL::Version::None, GL::Shader::Type::Vertex};
685686
/* [GenericGL-custom-preprocessor] */
686-
vert.addSource(Utility::formatString(
687+
vert.addSource(Utility::format(
687688
"#define POSITION_ATTRIBUTE_LOCATION {}\n"
688689
"#define NORMAL_ATTRIBUTE_LOCATION {}\n",
689690
Shaders::GenericGL3D::Position::Location,

Diff for: src/Magnum/DebugTools/TextureImage.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
#if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_GLES2)
3838
#include <Corrade/Containers/Iterable.h>
3939
#include <Corrade/Containers/Reference.h>
40-
#include <Corrade/Containers/StringStl.h> /** @todo remove once GL::Shader is <string>-free */
4140
#include <Corrade/Utility/Resource.h>
4241

4342
#include "Magnum/GL/AbstractShaderProgram.h"

Diff for: src/Magnum/GL/AbstractShaderProgram.cpp

+67-54
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,12 @@
2929

3030
#include <Corrade/Containers/Array.h>
3131
#include <Corrade/Containers/Iterable.h>
32-
#include <Corrade/Containers/StridedArrayView.h>
33-
#ifndef MAGNUM_TARGET_WEBGL
34-
#include <Corrade/Containers/String.h>
35-
#endif
36-
#include <Corrade/Containers/StringStl.h> /** @todo remove once <string>-free */
3732
#ifdef MAGNUM_BUILD_DEPRECATED
3833
#include <Corrade/Containers/Reference.h>
3934
#endif
40-
#include <Corrade/Utility/DebugStl.h>
41-
#include <Corrade/Utility/String.h>
35+
#include <Corrade/Containers/StridedArrayView.h>
36+
#include <Corrade/Containers/String.h>
37+
#include <Corrade/Containers/StringIterable.h>
4238

4339
#include "Magnum/GL/Context.h"
4440
#include "Magnum/GL/Extensions.h"
@@ -343,26 +339,29 @@ AbstractShaderProgram& AbstractShaderProgram::setLabel(const Containers::StringV
343339
}
344340
#endif
345341

346-
std::pair<bool, std::string> AbstractShaderProgram::validate() {
342+
std::pair<bool, Containers::String> AbstractShaderProgram::validate() {
347343
glValidateProgram(_id);
348344

349345
/* Check validation status */
350346
GLint success, logLength;
351347
glGetProgramiv(_id, GL_VALIDATE_STATUS, &success);
352348
glGetProgramiv(_id, GL_INFO_LOG_LENGTH, &logLength);
353349

354-
/* Error or warning message. The string is returned null-terminated, scrap
355-
the \0 at the end afterwards */
356-
std::string message(logLength, '\n');
357-
if(message.size() > 1)
358-
glGetProgramInfoLog(_id, message.size(), nullptr, &message[0]);
359-
message.resize(Math::max(logLength, 1)-1);
350+
/* Error or warning message. The length is reported including the null
351+
terminator and the string implicitly has a storage for that, thus
352+
specify one byte less. */
353+
Containers::String message{NoInit, std::size_t(Math::max(logLength, 1) - 1)};
354+
if(logLength > 1)
355+
glGetProgramInfoLog(_id, logLength, nullptr, message.data());
360356

361357
/* On some drivers (such as SwiftShader) the message contains a newline at
362358
the end, on some (such as Mesa) it doesn't. Same as with link() or
363359
compile() message trimming it doesn't make sense to add driver-specific
364360
workarounds for this, so just trim it always. */
365-
return {success, Utility::String::trim(std::move(message))};
361+
/** @todo this allocates a new string, revisit once String is capable of
362+
trimming in-place, e.g. `std::move(message).trimmed()` would just
363+
shift the data around */
364+
return {success, message.trimmed()};
366365
}
367366

368367
AbstractShaderProgram& AbstractShaderProgram::draw(Mesh& mesh) {
@@ -547,48 +546,62 @@ void AbstractShaderProgram::attachShaders(const Containers::Iterable<Shader>& sh
547546
for(Shader& s: shaders) attachShader(s);
548547
}
549548

550-
void AbstractShaderProgram::bindAttributeLocationInternal(const UnsignedInt location, const Containers::ArrayView<const char> name) {
551-
glBindAttribLocation(_id, location, name);
549+
void AbstractShaderProgram::bindAttributeLocation(const UnsignedInt location, const Containers::StringView name) {
550+
glBindAttribLocation(_id, location, Containers::String::nullTerminatedView(name).data());
552551
}
553552

554553
#ifndef MAGNUM_TARGET_GLES
555-
void AbstractShaderProgram::bindFragmentDataLocationInternal(const UnsignedInt location, const Containers::ArrayView<const char> name) {
556-
glBindFragDataLocation(_id, location, name);
554+
void AbstractShaderProgram::bindFragmentDataLocation(const UnsignedInt location, const Containers::StringView name) {
555+
glBindFragDataLocation(_id, location, Containers::String::nullTerminatedView(name).data());
557556
}
558-
void AbstractShaderProgram::bindFragmentDataLocationIndexedInternal(const UnsignedInt location, UnsignedInt index, const Containers::ArrayView<const char> name) {
559-
glBindFragDataLocationIndexed(_id, location, index, name);
557+
void AbstractShaderProgram::bindFragmentDataLocationIndexed(const UnsignedInt location, UnsignedInt index, const Containers::StringView name) {
558+
glBindFragDataLocationIndexed(_id, location, index, Containers::String::nullTerminatedView(name).data());
560559
}
561560
#endif
562561

563562
#ifndef MAGNUM_TARGET_GLES2
564-
void AbstractShaderProgram::setTransformFeedbackOutputs(const Containers::ArrayView<const std::string> outputs, const TransformFeedbackBufferMode bufferMode) {
563+
void AbstractShaderProgram::setTransformFeedbackOutputs(const Containers::StringIterable& outputs, const TransformFeedbackBufferMode bufferMode) {
565564
(this->*Context::current().state().shaderProgram.transformFeedbackVaryingsImplementation)(outputs, bufferMode);
566565
}
567566

568-
void AbstractShaderProgram::setTransformFeedbackOutputs(const std::initializer_list<std::string> outputs, const TransformFeedbackBufferMode bufferMode) {
569-
setTransformFeedbackOutputs(Containers::arrayView(outputs), bufferMode);
570-
}
571-
572-
void AbstractShaderProgram::transformFeedbackVaryingsImplementationDefault(const Containers::ArrayView<const std::string> outputs, const TransformFeedbackBufferMode bufferMode) {
573-
/** @todo VLAs */
574-
Containers::Array<const char*> names{outputs.size()};
575-
576-
Int i = 0;
577-
for(const std::string& output: outputs) names[i++] = output.data();
567+
void AbstractShaderProgram::transformFeedbackVaryingsImplementationDefault(const Containers::StringIterable& outputs, const TransformFeedbackBufferMode bufferMode) {
568+
Containers::ArrayView<Containers::String> nameData;
569+
Containers::ArrayView<const char*> names;
570+
Containers::ArrayTuple data{
571+
{NoInit, outputs.size(), nameData},
572+
{NoInit, outputs.size(), names}
573+
};
574+
for(std::size_t i = 0; i != outputs.size(); ++i) {
575+
new(&nameData[i]) Containers::String{Containers::String::nullTerminatedView(outputs[i])};
576+
names[i] = nameData[i].data();
577+
}
578578

579579
glTransformFeedbackVaryings(_id, outputs.size(), names, GLenum(bufferMode));
580580
}
581581

582582
#ifdef CORRADE_TARGET_WINDOWS
583-
void AbstractShaderProgram::transformFeedbackVaryingsImplementationDanglingWorkaround(const Containers::ArrayView<const std::string> outputs, const TransformFeedbackBufferMode bufferMode) {
583+
void AbstractShaderProgram::transformFeedbackVaryingsImplementationDanglingWorkaround(const Containers::StringIterable& outputs, const TransformFeedbackBufferMode bufferMode) {
584584
/* NVidia on Windows doesn't copy the names when calling
585-
glTransformFeedbackVaryings() so it then fails at link time because the
586-
char* are dangling. We have to do the copy on the engine side and keep
587-
the values until link time (which can happen any time and multiple
588-
times, so basically for the remaining lifetime of the shader program) */
589-
_transformFeedbackVaryingNames.assign(outputs.begin(), outputs.end());
585+
glTransformFeedbackVaryings() so it then might fail at link time because
586+
the char* get dangling. We have to do the copy on the engine side and
587+
keep the values until link time (which can happen any time and multiple
588+
times, so basically for the remaining lifetime of the shader program).
589+
590+
Compared to the above case we thus do a copy also if the view is not
591+
global in addition to not null-terminated, and we keep the string memory
592+
in a member variable. */
593+
Containers::ArrayView<Containers::String> nameData;
594+
Containers::ArrayView<const char*> names;
595+
_transformFeedbackVaryingNames = Containers::ArrayTuple{
596+
{NoInit, outputs.size(), nameData},
597+
{NoInit, outputs.size(), names}
598+
};
599+
for(std::size_t i = 0; i != outputs.size(); ++i) {
600+
new(&nameData[i]) Containers::String{Containers::String::nullTerminatedGlobalView(outputs[i])};
601+
names[i] = nameData[i].data();
602+
}
590603

591-
transformFeedbackVaryingsImplementationDefault({_transformFeedbackVaryingNames.data(), _transformFeedbackVaryingNames.size()}, bufferMode);
604+
glTransformFeedbackVaryings(_id, outputs.size(), names, GLenum(bufferMode));
592605
}
593606
#endif
594607
#endif
@@ -613,12 +626,12 @@ bool AbstractShaderProgram::checkLink(const Containers::Iterable<Shader>& shader
613626
glGetProgramiv(_id, GL_LINK_STATUS, &success);
614627
glGetProgramiv(_id, GL_INFO_LOG_LENGTH, &logLength);
615628

616-
/* Error or warning message. The string is returned null-terminated,
617-
strip the \0 at the end afterwards. */
618-
std::string message(logLength, '\n');
619-
if(message.size() > 1)
620-
glGetProgramInfoLog(_id, message.size(), nullptr, &message[0]);
621-
message.resize(Math::max(logLength, 1)-1);
629+
/* Error or warning message. The length is reported including the null
630+
terminator and the string implicitly has a storage for that, thus
631+
specify one byte less. */
632+
Containers::String message{NoInit, std::size_t(Math::max(logLength, 1)) - 1};
633+
if(logLength > 1)
634+
glGetProgramInfoLog(_id, logLength, nullptr, message.data());
622635

623636
/* Some drivers are chatty and can't keep shut when there's nothing to
624637
be said, handle that as well. */
@@ -662,16 +675,16 @@ bool AbstractShaderProgram::isLinkFinished() {
662675
return success == GL_TRUE;
663676
}
664677

665-
void AbstractShaderProgram::cleanLogImplementationNoOp(std::string&) {}
678+
void AbstractShaderProgram::cleanLogImplementationNoOp(Containers::String&) {}
666679

667680
#if defined(CORRADE_TARGET_WINDOWS) && !defined(MAGNUM_TARGET_GLES)
668-
void AbstractShaderProgram::cleanLogImplementationIntelWindows(std::string& message) {
681+
void AbstractShaderProgram::cleanLogImplementationIntelWindows(Containers::String& message) {
669682
if(message == "No errors.\n") message = {};
670683
}
671684
#endif
672685

673686
#if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL)
674-
void AbstractShaderProgram::cleanLogImplementationAngle(std::string& message) {
687+
void AbstractShaderProgram::cleanLogImplementationAngle(Containers::String& message) {
675688
if(message == "\n") message = {};
676689
}
677690
#endif
@@ -680,18 +693,18 @@ void AbstractShaderProgram::completionStatusImplementationFallback(GLuint, GLenu
680693
*value = GL_TRUE;
681694
}
682695

683-
Int AbstractShaderProgram::uniformLocationInternal(const Containers::ArrayView<const char> name) {
684-
const GLint location = glGetUniformLocation(_id, name);
696+
Int AbstractShaderProgram::uniformLocation(const Containers::StringView name) {
697+
const GLint location = glGetUniformLocation(_id, Containers::String::nullTerminatedView(name).data());
685698
if(location == -1)
686-
Warning{} << "GL::AbstractShaderProgram: location of uniform \'" << Debug::nospace << std::string{name, name.size()} << Debug::nospace << "\' cannot be retrieved";
699+
Warning{} << "GL::AbstractShaderProgram: location of uniform \'" << Debug::nospace << name << Debug::nospace << "\' cannot be retrieved";
687700
return location;
688701
}
689702

690703
#ifndef MAGNUM_TARGET_GLES2
691-
UnsignedInt AbstractShaderProgram::uniformBlockIndexInternal(const Containers::ArrayView<const char> name) {
692-
const GLuint index = glGetUniformBlockIndex(_id, name);
704+
UnsignedInt AbstractShaderProgram::uniformBlockIndex(const Containers::StringView name) {
705+
const GLuint index = glGetUniformBlockIndex(_id, Containers::String::nullTerminatedView(name).data());
693706
if(index == GL_INVALID_INDEX)
694-
Warning{} << "GL::AbstractShaderProgram: index of uniform block \'" << Debug::nospace << std::string{name, name.size()} << Debug::nospace << "\' cannot be retrieved";
707+
Warning{} << "GL::AbstractShaderProgram: index of uniform block \'" << Debug::nospace << name << Debug::nospace << "\' cannot be retrieved";
695708
return index;
696709
}
697710
#endif

0 commit comments

Comments
 (0)