Skip to content

Commit

Permalink
Skip a copy entirely when updating per-renderable ubo
Browse files Browse the repository at this point in the history
We now fill the buffer directly into the command stream
which avoids an extra copy of the data.
  • Loading branch information
pixelflinger committed Oct 9, 2018
1 parent b29432e commit 95a2686
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 25 deletions.
32 changes: 17 additions & 15 deletions filament/src/Scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,27 +160,29 @@ void FScene::prepare(const math::mat4f& worldOriginTansform) {

void FScene::updateUBOs(utils::Range<uint32_t> visibleRenderables) noexcept {
FEngine::DriverApi& driver = mEngine.getDriverApi();
UniformBuffer& uniformBuffer = mUniformBuffer;
Handle<HwUniformBuffer>& uniformBufferHandle = mUniformBufferHandle;
const size_t size = sizeof(FRenderableManager::Transform) * visibleRenderables.size();
const size_t size = visibleRenderables.size() * sizeof(FRenderableManager::Transform);

// reallocate UBO if it's too small
if (uniformBuffer.getSize() < size) {
// allocate 1/3 extra
size_t newSize = (4 * visibleRenderables.size() + 2) / 3;
uniformBuffer = UniformBuffer(newSize * sizeof(FRenderableManager::Transform));
if (uniformBufferHandle) {
driver.destroyUniformBuffer(uniformBufferHandle);
}
uniformBufferHandle = driver.createUniformBuffer(uniformBuffer.getSize());
if (mUboSize < size) {
// allocate 1/3 extra, with a minimum of 16 objects
const size_t count = std::max(size_t(16u), (4u * visibleRenderables.size() + 2u) / 3u);
mUboSize = uint32_t(count * sizeof(FRenderableManager::Transform));
driver.destroyUniformBuffer(uniformBufferHandle);
uniformBufferHandle = driver.createUniformBuffer(mUboSize);
} else {
// should we shrink the underlying UBO at some point?
}

// allocate space into the command stream directly
void* const buffer = driver.allocate(size);

auto& sceneData = mRenderableData;
for (uint32_t i : visibleRenderables) {
mat4f const& model = sceneData.elementAt<WORLD_TRANSFORM>(i);
const size_t offset = sizeof(FRenderableManager::Transform) * i;
const size_t offset = i * sizeof(FRenderableManager::Transform);

uniformBuffer.setUniform(
UniformBuffer::setUniform(buffer,
offset + offsetof(FRenderableManager::Transform, worldFromModelMatrix),
model);

Expand All @@ -189,13 +191,13 @@ void FScene::updateUBOs(utils::Range<uint32_t> visibleRenderables) noexcept {
// in the shader (that's already the case anyways, since normalization is needed after
// interpolation).
// Note: if the model matrix is known to be a rigid-transform, we could just use it directly.
uniformBuffer.setUniform(
UniformBuffer::setUniform(buffer,
offset + offsetof(FRenderableManager::Transform, worldFromModelNormalMatrix),
transpose(inverse(model.upperLeft())));
}

driver.updateUniformBuffer(uniformBufferHandle, uniformBuffer.toBufferDescriptor(driver, 0, size));
uniformBuffer.clean();
// TODO: handle static objects separately
driver.updateUniformBuffer(uniformBufferHandle, { buffer, size });
}

void FScene::terminate(FEngine& engine) {
Expand Down
23 changes: 14 additions & 9 deletions filament/src/UniformBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,18 @@ class UniformBuffer {
std::copy_n(begin, count, p);
}

template <typename T, typename = typename is_supported_type<T>::type>
static void setUniform(void* addr, size_t offset, const T& v) noexcept {
addr = static_cast<char*>(addr) + offset;
T* p = static_cast<T*>(addr);
*p = v;
}

// set uniform of known types to the proper offset (e.g.: use offsetof())
// (see specialization for mat3f below)
template <typename T, typename = typename is_supported_type<T>::type>
void setUniform(size_t offset, const T& v) noexcept {
T* p = static_cast<T*>(invalidateUniforms(offset, sizeof(T)));
*p = v;
setUniform(invalidateUniforms(offset, sizeof(T)), 0, v);
}

// get uniform of known types from the proper offset (e.g.: use offsetof())
Expand All @@ -152,8 +158,7 @@ class UniformBuffer {
}
}

driver::BufferDescriptor toBufferDescriptor(
driver::DriverApi& driver) const noexcept {
driver::BufferDescriptor toBufferDescriptor(driver::DriverApi& driver) const noexcept {
driver::BufferDescriptor p;
p.size = getSize();
p.buffer = driver.allocate(p.size);
Expand Down Expand Up @@ -203,10 +208,13 @@ UniformBuffer::setUniformArray(size_t offset, math::float3 const* begin, size_t

// specialization for mat3f (which has a different alignment, see std140 layout rules)
template<>
inline void UniformBuffer::setUniform(size_t offset, const math::mat3f& v) noexcept {
inline void UniformBuffer::setUniform(void* addr, size_t offset, const math::mat3f& v) noexcept {
struct mat43 {
float v[3][4];
} temp;
};

addr = static_cast<char*>(addr) + offset;
mat43& temp = *static_cast<mat43*>(addr);

temp.v[0][0] = v[0][0];
temp.v[0][1] = v[0][1];
Expand All @@ -222,9 +230,6 @@ inline void UniformBuffer::setUniform(size_t offset, const math::mat3f& v) noexc
temp.v[2][1] = v[2][1];
temp.v[2][2] = v[2][2];
temp.v[2][3] = 0; // not needed, but doesn't cost anything

// this is like setUniform(), except its not a "supported_type"
*static_cast<mat43*>(invalidateUniforms(offset, sizeof(temp))) = temp;
}

} // namespace filament
Expand Down
2 changes: 1 addition & 1 deletion filament/src/details/Scene.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ class FScene : public Scene {
tsl::robin_set<utils::Entity> mEntities;
RenderableSoa mRenderableData;
LightSoa mLightData;
UniformBuffer mUniformBuffer;
uint32_t mUboSize = 0;
filament::Handle<HwUniformBuffer> mUniformBufferHandle;
};

Expand Down

0 comments on commit 95a2686

Please sign in to comment.