0032 - Constructors
Status | Under Review |
---|---|
Author | |
Sponsor |
- Planned Version: 202y
Introduction
As user defined data structures get more common and complex, the absence of constructors and destructors is more and more awkward. This proposal seeks to bring the full features of C++ initialization and destruction into HLSL.
Motivation
Constructors are incredibly useful for ensuring data initialization and implementing common patterns like Resource-Acquisition-Is-Initialization (RAII).
Proposed solution
Introduce the following features to HLSL:
- C++ Constructors and Destructors
- C++ 11 Delegating Constructors
- C++ 11 Default and Deleted methods
- C++ 11 Default initializers
- Clang attribute((constructor)) and attribute((destructor))
Constructor syntax will match C++ 11 constructor syntax including support for delegating constructors. It will be available on all user-defined data types that support constructors in C++.
Detailed design
Global Construction
HLSL global constructors will be ordered following C++ conventions. Constant-evaluated initializers occur first, followed by non-constant initializers which are ordered by declaration order in the translation unit.
Each entry function will call the global constructors before executing any further instructions and the global destructor immediately before returning.
In library shaders, the entry translation unit’s global constructors are executed after the linked library global constructors, and the entry’s global destructors are executed before linked library global destructors.
Non-trivial global constructors will not be stripped from library shaders. Meaning, an unused global with a non-trivial constructor will be linked into the final linked shader binary. This conforms to the C++ standard definitions for static storage duration (see: ISO C++ Standard basic.stc.static).
Due to the inherent performance drawbacks of global construction, warnings will be issued by default for any code that introduces a non-trivial global constructor.
Constructing Groupshared
Constructor initialization of groupshared
objects will be followed by a memory
barrier and group sync. This comes with inherent performance costs, so shader
authors should use constructors with groupshared
objects sparingly.
A groupshared
user defined object that does not have a constructor, or has a
constructor that does not initialize any of its fields, will not incur the
memory barrier.
Due to the inherent performance drawbacks of groupshared
construction,
warnings will be issued by default for any code that introduces a groupshared
object constructor.
Constructors and Semantic Annotations
When a user defined data type contains members which are annotated with semantic attributes and the data structure is initialized as a global or as a parameter to the entry function, the semantic will be applied to the field after the default constructor executes.
For example:
struct VSIn {
uint ID : SV_VertexID;
float F;
VSIn() : ID(0), F(1.0) {}
};
float4 main(in VSIn In) : SV_Position
{
// In.vid is SV_VertexID, In.f is 1.0
VSIn In2;
// In2.vid is 0, In.f is 1.0
}
Currently HLSL requires that all members of a user defined data type passed into an entry function must have semantic annotations to initialize the structure. With the addition of constructors and default value initialization, HLSL will instead require that every field either have a semantic annotation, or a default initialization through either a default constructor or a default initializer.
HLSL will also introduce default initialization and defaulted and deleted constructors. Default initialization is incompatible with HLSL annotations, and can only be used on variable declarations that either use C++ attributes for annotations, or have no annotation (see: proposal for C++ Attributes).
HLSL will adopt C++ rules for implicitly defining special member functions as
defined in the C++ 11 specification under
[special]
. This will
include a default constructor, destructor, copy constructor, move constructor,
and copy assignment operator, and move assignment operator.
For example:
struct VSIn {
uint ID : SV_VertexID = 2; // invalid
uint ID = 2 : SV_VertexID; // invalid
[[hlsl::SV_VertexID]] uint ID = 2; // valid
float F = 1.0; // valid
VSIn() = default;
};
float4 main(in VSIn In) : SV_Position
{
// In.vid is SV_VertexID, In.f is 1.0
VSIn In2;
// In2.vid is 0, In.f is 1.0
}