Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add push constants #7817

Merged
merged 8 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 8 additions & 2 deletions filament/backend/include/backend/DriverEnums.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

#include <array> // FIXME: STL headers are not allowed in public headers
#include <type_traits> // FIXME: STL headers are not allowed in public headers
#include <variant> // FIXME: STL headers are not allowed in public headers

#include <stddef.h>
#include <stdint.h>
Expand Down Expand Up @@ -91,12 +92,15 @@ static constexpr uint64_t SWAP_CHAIN_HAS_STENCIL_BUFFER = SWAP_CHAIN_CON
*/
static constexpr uint64_t SWAP_CHAIN_CONFIG_PROTECTED_CONTENT = 0x40;


static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_SAMPLER_COUNT = 62; // Maximum needed at feature level 3.
static constexpr size_t MAX_VERTEX_BUFFER_COUNT = 16; // Max number of bound buffer objects.
static constexpr size_t MAX_SSBO_COUNT = 4; // This is guaranteed by OpenGL ES.

static constexpr size_t MAX_PUSH_CONSTANT_COUNT = 32; // Vulkan 1.1 spec allows for 128-byte
// of push constant (we assume 4-byte
// types).

// Per feature level caps
// Use (int)FeatureLevel to index this array
static constexpr struct {
Expand Down Expand Up @@ -332,7 +336,7 @@ enum class UniformType : uint8_t {
/**
* Supported constant parameter types
*/
enum class ConstantType : uint8_t {
enum class ConstantType : uint8_t {
INT,
FLOAT,
BOOL
Expand Down Expand Up @@ -1219,6 +1223,8 @@ struct StencilState {
uint8_t padding = 0;
};

using PushConstantVariant = std::variant<int32_t, float, bool>;

static_assert(sizeof(StencilState::StencilOperations) == 5u,
"StencilOperations size not what was intended");

Expand Down
19 changes: 18 additions & 1 deletion filament/backend/include/backend/Program.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ class Program {
static constexpr size_t SHADER_TYPE_COUNT = 3;
static constexpr size_t UNIFORM_BINDING_COUNT = CONFIG_UNIFORM_BINDING_COUNT;
static constexpr size_t SAMPLER_BINDING_COUNT = CONFIG_SAMPLER_BINDING_COUNT;

struct Sampler {
utils::CString name = {}; // name of the sampler in the shader
uint32_t binding = 0; // binding point of the sampler in the shader
Expand Down Expand Up @@ -117,6 +116,14 @@ class Program {
Program& specializationConstants(
utils::FixedCapacityVector<SpecializationConstant> specConstants) noexcept;

struct PushConstant {
utils::CString name;
ConstantType type;
};

Program& pushConstants(ShaderStage stage,
utils::FixedCapacityVector<PushConstant> constants) noexcept;

Program& cacheId(uint64_t cacheId) noexcept;

Program& multiview(bool multiview) noexcept;
Expand Down Expand Up @@ -148,6 +155,15 @@ class Program {
return mSpecializationConstants;
}

utils::FixedCapacityVector<PushConstant> const& getPushConstants(
ShaderStage stage) const noexcept {
return mPushConstants[static_cast<uint8_t>(stage)];
}

utils::FixedCapacityVector<PushConstant>& getPushConstants(ShaderStage stage) noexcept {
return mPushConstants[static_cast<uint8_t>(stage)];
}

uint64_t getCacheId() const noexcept { return mCacheId; }

bool isMultiview() const noexcept { return mMultiview; }
Expand All @@ -165,6 +181,7 @@ class Program {
uint64_t mCacheId{};
utils::Invocable<utils::io::ostream&(utils::io::ostream& out)> mLogger;
utils::FixedCapacityVector<SpecializationConstant> mSpecializationConstants;
std::array<utils::FixedCapacityVector<PushConstant>, SHADER_TYPE_COUNT> mPushConstants;
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> mAttributes;
std::array<UniformInfo, Program::UNIFORM_BINDING_COUNT> mBindingUniformInfo;
CompilerPriorityQueue mPriorityQueue = CompilerPriorityQueue::HIGH;
Expand Down
5 changes: 5 additions & 0 deletions filament/backend/include/private/backend/DriverAPI.inc
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,11 @@ DECL_DRIVER_API_N(bindSamplers,
uint32_t, index,
backend::SamplerGroupHandle, sbh)

DECL_DRIVER_API_N(setPushConstant,
backend::ShaderStage, stage,
uint8_t, index,
backend::PushConstantVariant, value)
poweifeng marked this conversation as resolved.
Show resolved Hide resolved

DECL_DRIVER_API_N(insertEventMarker,
const char*, string,
uint32_t, len = 0)
Expand Down
6 changes: 6 additions & 0 deletions filament/backend/src/Program.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ Program& Program::specializationConstants(
return *this;
}

Program& Program::pushConstants(ShaderStage stage,
utils::FixedCapacityVector<PushConstant> constants) noexcept {
mPushConstants[static_cast<uint8_t>(stage)] = std::move(constants);
return *this;
}

Program& Program::cacheId(uint64_t cacheId) noexcept {
mCacheId = cacheId;
return *this;
Expand Down
3 changes: 3 additions & 0 deletions filament/backend/src/metal/MetalDriver.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1241,6 +1241,9 @@
mContext->samplerBindings[index] = sb;
}

void MetalDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
backend::PushConstantVariant value) {}

void MetalDriver::insertEventMarker(const char* string, uint32_t len) {

}
Expand Down
4 changes: 4 additions & 0 deletions filament/backend/src/noop/NoopDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,10 @@ void NoopDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index) {
void NoopDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
}

void NoopDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
backend::PushConstantVariant value) {
}

void NoopDriver::insertEventMarker(char const* string, uint32_t len) {
}

Expand Down
37 changes: 37 additions & 0 deletions filament/backend/src/opengl/OpenGLDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,10 @@ void OpenGLDriver::terminate() {
assert_invariant(mGpuCommandCompleteOps.empty());
#endif

if (mCurrentPushConstants) {
delete mCurrentPushConstants;
}

mContext.terminate();

mPlatform.terminate();
Expand All @@ -285,6 +289,33 @@ void OpenGLDriver::bindSampler(GLuint unit, GLuint sampler) noexcept {
mContext.bindSampler(unit, sampler);
}

void OpenGLDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
backend::PushConstantVariant value) {
poweifeng marked this conversation as resolved.
Show resolved Hide resolved
assert_invariant(mCurrentPushConstants &&
"Calling setPushConstant() before binding a pipeline");

auto const& [location, type] = mCurrentPushConstants->get(stage, index);

// This push constant wasn't found in the shader. It's ok to return without error-ing here.
if (location < 0) {
return;
}

if (std::holds_alternative<bool>(value)) {
assert_invariant(type == ConstantType::BOOL);
bool const bval = std::get<bool>(value);
glUniform1i(location, bval ? 1 : 0);
} else if (std::holds_alternative<float>(value)) {
assert_invariant(type == ConstantType::FLOAT);
float const fval = std::get<float>(value);
glUniform1f(location, fval);
} else {
assert_invariant(type == ConstantType::INT);
int const ival = std::get<int>(value);
glUniform1i(location, ival);
}
}

void OpenGLDriver::bindTexture(GLuint unit, GLTexture const* t) noexcept {
assert_invariant(t != nullptr);
mContext.bindTexture(unit, t->gl.target, t->gl.id);
Expand Down Expand Up @@ -3802,6 +3833,12 @@ void OpenGLDriver::bindPipeline(PipelineState state) {
gl.polygonOffset(state.polygonOffset.slope, state.polygonOffset.constant);
OpenGLProgram* const p = handle_cast<OpenGLProgram*>(state.program);
mValidProgram = useProgram(p);

if (!mCurrentPushConstants) {
mCurrentPushConstants = new (std::nothrow) PushConstantBundle{p->getPushConstants()};
} else {
(*mCurrentPushConstants) = p->getPushConstants();
}
Comment on lines +3851 to +3856
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just use a value instead of a pointer since it will always be allocated, this will avoid a if here the first time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's ok to pull in OpenGLProgram.h in OpenGLDriver.h?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that should be okay.

}

void OpenGLDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
Expand Down
4 changes: 4 additions & 0 deletions filament/backend/src/opengl/OpenGLDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ struct TargetBufferInfo;
class OpenGLProgram;
class TimerQueryFactoryInterface;

struct PushConstantBundle;

class OpenGLDriver final : public DriverBase {
inline explicit OpenGLDriver(OpenGLPlatform* platform,
const Platform::DriverConfig& driverConfig) noexcept;
Expand Down Expand Up @@ -375,6 +377,8 @@ class OpenGLDriver final : public DriverBase {
// for ES2 sRGB support
GLSwapChain* mCurrentDrawSwapChain = nullptr;
bool mRec709OutputColorspace = false;

PushConstantBundle* mCurrentPushConstants = nullptr;
};

// ------------------------------------------------------------------------------------------------
Expand Down
43 changes: 40 additions & 3 deletions filament/backend/src/opengl/OpenGLProgram.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,14 @@ struct OpenGLProgram::LazyInitializationData {
Program::UniformBlockInfo uniformBlockInfo;
Program::SamplerGroupInfo samplerGroupInfo;
std::array<Program::UniformInfo, Program::UNIFORM_BINDING_COUNT> bindingUniformInfo;
utils::FixedCapacityVector<Program::PushConstant> pushConstants;
uint8_t pushConstantFragmentStageOffset;
poweifeng marked this conversation as resolved.
Show resolved Hide resolved
};


OpenGLProgram::OpenGLProgram() noexcept = default;

OpenGLProgram::OpenGLProgram(OpenGLDriver& gld, Program&& program) noexcept
: HwProgram(std::move(program.getName())) {

auto* const lazyInitializationData = new(std::nothrow) LazyInitializationData();
lazyInitializationData->samplerGroupInfo = std::move(program.getSamplerGroupInfo());
if (UTILS_UNLIKELY(gld.getContext().isES2())) {
Expand All @@ -62,6 +62,30 @@ OpenGLProgram::OpenGLProgram(OpenGLDriver& gld, Program&& program) noexcept
lazyInitializationData->uniformBlockInfo = std::move(program.getUniformBlockBindings());
}

lazyInitializationData->pushConstantFragmentStageOffset = 0;

auto& vertexConstants = program.getPushConstants(ShaderStage::VERTEX);
auto& fragmentConstants = program.getPushConstants(ShaderStage::FRAGMENT);

size_t const totalConstantCount = vertexConstants.size() + fragmentConstants.size();
if (totalConstantCount > 0) {
auto& allConstants = lazyInitializationData->pushConstants;
size_t const numVertexConstants = vertexConstants.size();

if (numVertexConstants > 0) {
allConstants = std::move(vertexConstants);
lazyInitializationData->pushConstantFragmentStageOffset = numVertexConstants;
}

allConstants.reserve(totalConstantCount);
allConstants.resize(totalConstantCount);

std::for_each(fragmentConstants.cbegin(), fragmentConstants.cend(),
[&allConstants](Program::PushConstant const& constant) {
allConstants.push_back(constant);
});
}

ShaderCompilerService& compiler = gld.getShaderCompilerService();
mToken = compiler.createProgram(name, std::move(program));

Expand Down Expand Up @@ -89,7 +113,6 @@ OpenGLProgram::~OpenGLProgram() noexcept {
}

void OpenGLProgram::initialize(OpenGLDriver& gld) {

SYSTRACE_CALL();

assert_invariant(gl.program == 0);
Expand Down Expand Up @@ -203,6 +226,20 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
}
}
mUsedBindingsCount = usedBindingCount;

// Push constant initialization
mPushConstantFragmentStageOffset = lazyInitializationData.pushConstantFragmentStageOffset;
auto const& constants = lazyInitializationData.pushConstants;
if (!constants.empty()) {
mPushConstants.reserve(constants.size());
mPushConstants.resize(constants.size());

std::transform(constants.cbegin(), constants.cend(), mPushConstants.begin(),
[program](Program::PushConstant const& constant) -> std::pair<GLint, ConstantType> {
GLint const loc = glGetUniformLocation(program, constant.name.c_str());
return {loc, constant.type};
});
}
}

void OpenGLProgram::updateSamplers(OpenGLDriver* const gld) const noexcept {
Expand Down
39 changes: 35 additions & 4 deletions filament/backend/src/opengl/OpenGLProgram.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,24 @@ namespace filament::backend {

class OpenGLDriver;

struct PushConstantBundle {
uint8_t fragmentStageOffset = 0;
utils::FixedCapacityVector<std::pair<GLint, ConstantType>> const* constants = nullptr;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can even make this a reference

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

used two slices instead.


inline std::pair<GLint, ConstantType> const& get(ShaderStage stage, uint8_t index) const {
assert_invariant(stage == ShaderStage::VERTEX ||stage == ShaderStage::FRAGMENT);

// Either we're asking for a fragment stage constant or we're asking for a vertex stage
// constant and the number of vertex stage constants is greater than 0.
assert_invariant(stage == ShaderStage::FRAGMENT || fragmentStageOffset > 0);

uint8_t const offset = (stage == ShaderStage::VERTEX ? 0 : fragmentStageOffset) + index;

assert_invariant(offset < constants->size());
return (*constants)[offset];
}
};

class OpenGLProgram : public HwProgram {
public:

Expand Down Expand Up @@ -78,6 +96,13 @@ class OpenGLProgram : public HwProgram {
GLuint program = 0;
} gl; // 4 bytes

PushConstantBundle getPushConstants() {
poweifeng marked this conversation as resolved.
Show resolved Hide resolved
return {
.fragmentStageOffset = mPushConstantFragmentStageOffset,
.constants = &mPushConstants,
};
}

private:
// keep these away from of other class attributes
struct LazyInitializationData;
Expand All @@ -95,23 +120,29 @@ class OpenGLProgram : public HwProgram {
ShaderCompilerService::program_token_t mToken{}; // 16 bytes

uint8_t mUsedBindingsCount = 0u; // 1 byte
UTILS_UNUSED uint8_t padding[3] = {}; // 3 bytes
UTILS_UNUSED uint8_t padding[2] = {}; // 2 byte

// Push constant array offset for fragment stage constants.
uint8_t mPushConstantFragmentStageOffset = 0u; // 1 byte

// only needed for ES2
GLint mRec709Location = -1; // 4 bytes
GLint mRec709Location = -1; // 4 bytes

using LocationInfo = utils::FixedCapacityVector<GLint>;
struct UniformsRecord {
Program::UniformInfo uniforms;
LocationInfo locations;
mutable GLuint id = 0;
mutable uint16_t age = std::numeric_limits<uint16_t>::max();
};
UniformsRecord const* mUniformsRecords = nullptr;
UniformsRecord const* mUniformsRecords = nullptr; // 8 bytes

// Store [location, type] pairs.
utils::FixedCapacityVector<std::pair<GLint, ConstantType>> mPushConstants; // 16 bytes
poweifeng marked this conversation as resolved.
Show resolved Hide resolved
};

// if OpenGLProgram is larger tha 64 bytes, it'll fall in a larger Handle bucket.
static_assert(sizeof(OpenGLProgram) <= 64); // currently 48 bytes
static_assert(sizeof(OpenGLProgram) <= 64); // currently 64 bytes

} // namespace filament::backend

Expand Down