Fix updates hillshade geometry (#2842)

This commit is contained in:
Tim Sylvester
2024-09-23 08:36:00 -07:00
committed by GitHub
parent cadb8d1da0
commit 84ca231da5
28 changed files with 307 additions and 156 deletions

View File

@@ -25,6 +25,7 @@ class PaintParameters;
class PaintPropertyBindersBase;
enum class RenderPass : uint8_t;
class RenderTile;
class SegmentBase;
using LayerTweakerPtr = std::shared_ptr<LayerTweaker>;
using RenderTiles = std::shared_ptr<const std::vector<std::reference_wrapper<const RenderTile>>>;
@@ -192,7 +193,20 @@ public:
const gfx::VertexAttributeArrayPtr& getVertexAttributes() const noexcept { return vertexAttributes; }
/// Set vertex attribute array
void setVertexAttributes(gfx::VertexAttributeArrayPtr value) noexcept { vertexAttributes = std::move(value); }
void setVertexAttributes(gfx::VertexAttributeArrayPtr value) noexcept {
vertexAttributes = std::move(value);
// The attribute bindings need to be rebuilt, we can't rely on the update
// time check as these new values may not have been modified recently.
attributeUpdateTime.reset();
}
/// Update vertices, indices, and segments
virtual void updateVertexAttributes(gfx::VertexAttributeArrayPtr,
std::size_t vertexCount,
gfx::DrawMode,
gfx::IndexVectorBasePtr,
const SegmentBase* segments,
std::size_t segmentCount) = 0;
/// Get the instance attributes
const gfx::VertexAttributeArrayPtr& getInstanceAttributes() const noexcept { return instanceAttributes; }
@@ -261,6 +275,7 @@ public:
void setRenderTile(Immutable<std::vector<RenderTile>>, const RenderTile*);
const std::chrono::duration<double> createTime = util::MonotonicTimer::now();
std::optional<std::chrono::duration<double>> getAttributeUpdateTime() const { return attributeUpdateTime; }
protected:
bool enabled = true;
@@ -282,7 +297,7 @@ protected:
UniqueDrawableData drawableData{};
gfx::VertexAttributeArrayPtr vertexAttributes;
gfx::VertexAttributeArrayPtr instanceAttributes;
std::chrono::duration<double> attributeUpdateTime = util::MonotonicTimer::now();
std::optional<std::chrono::duration<double>> attributeUpdateTime;
struct Impl;
std::unique_ptr<Impl> impl;

View File

@@ -48,6 +48,13 @@ public:
void upload(gfx::UploadPass&);
void updateVertexAttributes(gfx::VertexAttributeArrayPtr,
std::size_t vertexCount,
gfx::DrawMode,
gfx::IndexVectorBasePtr,
const SegmentBase* segments,
std::size_t segmentCount) override;
protected:
class Impl;
const std::unique_ptr<Impl> impl;

View File

@@ -35,7 +35,7 @@ public:
static const std::vector<std::uint8_t>& getRaw(gfx::VertexAttribute& attr,
platform::GLenum,
std::chrono::duration<double> lastUpdate);
std::optional<std::chrono::duration<double>> lastUpdate);
private:
static int getSize(platform::GLenum glType);

View File

@@ -130,9 +130,6 @@ public:
std::size_t size,
bool persistent) override;
/// Get an empty buffer to act as a placeholder
const BufferResource& getEmptyBuffer();
/// Get a reusable buffer containing the standard fixed tile vertices (+/- `util::EXTENT`)
const BufferResource& getTileVertexBuffer();

View File

@@ -57,6 +57,13 @@ public:
void setSubLayerIndex(int32_t) override;
void setDepthType(gfx::DepthMaskType) override;
void updateVertexAttributes(gfx::VertexAttributeArrayPtr,
std::size_t vertexCount,
gfx::DrawMode,
gfx::IndexVectorBasePtr,
const SegmentBase* segments,
std::size_t segmentCount) override;
protected:
// For testing only.
Drawable(std::unique_ptr<Impl>);

View File

@@ -56,7 +56,9 @@ public:
void updateResource(BufferResource&, const void* data, std::size_t size);
const gfx::UniqueVertexBufferResource& getBuffer(const gfx::VertexVectorBasePtr&, gfx::BufferUsageType);
const gfx::UniqueVertexBufferResource& getBuffer(const gfx::VertexVectorBasePtr&,
gfx::BufferUsageType,
bool forceUpdate);
gfx::AttributeBindingArray buildAttributeBindings(
const std::size_t vertexCount,
@@ -66,7 +68,7 @@ public:
const gfx::VertexAttributeArray& defaults,
const gfx::VertexAttributeArray& overrides,
gfx::BufferUsageType,
const std::chrono::duration<double> lastUpdate,
const std::optional<std::chrono::duration<double>> lastUpdate,
/*out*/ std::vector<std::unique_ptr<gfx::VertexBufferResource>>& outBuffers) override;
std::unique_ptr<gfx::TextureResource> createTextureResource(Size,

View File

@@ -22,7 +22,8 @@ public:
static const gfx::UniqueVertexBufferResource& getBuffer(gfx::VertexAttribute&,
UploadPass&,
const gfx::BufferUsageType);
const gfx::BufferUsageType,
bool forceUpdate);
};
/// Stores a collection of vertex attributes by name

View File

@@ -44,6 +44,13 @@ public:
void setLineWidth(int32_t value) override;
void setCullFaceMode(const gfx::CullFaceMode&) override;
void updateVertexAttributes(gfx::VertexAttributeArrayPtr,
std::size_t vertexCount,
gfx::DrawMode,
gfx::IndexVectorBasePtr,
const SegmentBase* segments,
std::size_t segmentCount) override;
protected:
void buildVulkanInputBindings() noexcept;

View File

@@ -51,7 +51,9 @@ public:
bool persistent) override;
void updateIndexBufferResource(gfx::IndexBufferResource&, const void* data, std::size_t size) override;
const gfx::UniqueVertexBufferResource& getBuffer(const gfx::VertexVectorBasePtr&, gfx::BufferUsageType);
const gfx::UniqueVertexBufferResource& getBuffer(const gfx::VertexVectorBasePtr&,
gfx::BufferUsageType,
bool forceUpdate);
gfx::AttributeBindingArray buildAttributeBindings(
const std::size_t vertexCount,
@@ -61,7 +63,7 @@ public:
const gfx::VertexAttributeArray& defaults,
const gfx::VertexAttributeArray& overrides,
gfx::BufferUsageType,
const std::chrono::duration<double> lastUpdate,
const std::optional<std::chrono::duration<double>> lastUpdate,
/*out*/ std::vector<std::unique_ptr<gfx::VertexBufferResource>>& outBuffers) override;
std::unique_ptr<gfx::TextureResource> createTextureResource(Size,

View File

@@ -23,7 +23,8 @@ public:
static const gfx::UniqueVertexBufferResource& getBuffer(gfx::VertexAttribute&,
UploadPass&,
const gfx::BufferUsageType);
const gfx::BufferUsageType,
bool forceUpdate);
};
/// Stores a collection of vertex attributes by name

View File

@@ -87,7 +87,7 @@ public:
const gfx::VertexAttributeArray& defaults,
const gfx::VertexAttributeArray& overrides,
gfx::BufferUsageType,
const std::chrono::duration<double> lastUpdate,
const std::optional<std::chrono::duration<double>> lastUpdate,
/*out*/ std::vector<std::unique_ptr<gfx::VertexBufferResource>>& outBuffers) = 0;
#endif

View File

@@ -86,6 +86,36 @@ void DrawableGL::setIndexData(gfx::IndexVectorBasePtr indexes, std::vector<Uniqu
impl->segments = std::move(segments);
}
void DrawableGL::updateVertexAttributes(gfx::VertexAttributeArrayPtr vertices,
std::size_t vertexCount,
gfx::DrawMode mode,
gfx::IndexVectorBasePtr indexes,
const SegmentBase* segments,
std::size_t segmentCount) {
gfx::Drawable::setVertexAttributes(std::move(vertices));
impl->vertexCount = vertexCount;
std::vector<std::unique_ptr<Drawable::DrawSegment>> drawSegs;
drawSegs.reserve(segmentCount);
for (std::size_t i = 0; i < segmentCount; ++i) {
const auto& seg = segments[i];
auto segCopy = SegmentBase{
// no copy constructor
seg.vertexOffset,
seg.indexOffset,
seg.vertexLength,
seg.indexLength,
seg.sortKey,
};
auto drawSeg = std::make_unique<DrawableGL::DrawSegmentGL>(
mode, std::move(segCopy), VertexArray{{nullptr, false}});
drawSegs.push_back(std::move(drawSeg));
}
impl->indexes = std::move(indexes);
impl->segments = std::move(drawSegs);
}
void DrawableGL::setVertices(std::vector<uint8_t>&& data, std::size_t count, gfx::AttributeDataType type_) {
impl->vertexData = std::move(data);
impl->vertexCount = count;
@@ -166,7 +196,8 @@ void DrawableGL::upload(gfx::UploadPass& uploadPass) {
if (impl->indexes) {
impl->indexes->updateModified();
}
if (impl->indexes && (!impl->indexes->getBuffer() || impl->indexes->isModifiedAfter(attributeUpdateTime))) {
if (impl->indexes &&
(!impl->indexes->getBuffer() || !attributeUpdateTime || impl->indexes->isModifiedAfter(*attributeUpdateTime))) {
MLN_TRACE_ZONE(build indexes);
auto indexBufferResource{
uploadPass.createIndexBufferResource(impl->indexes->data(), impl->indexes->bytes(), usage)};
@@ -178,7 +209,7 @@ void DrawableGL::upload(gfx::UploadPass& uploadPass) {
// Build the vertex attributes and bindings, if necessary
if (impl->attributeBindings.empty() ||
(vertexAttributes && vertexAttributes->isModifiedAfter(attributeUpdateTime))) {
(vertexAttributes && (!attributeUpdateTime || vertexAttributes->isModifiedAfter(*attributeUpdateTime)))) {
MLN_TRACE_ZONE(build attributes);
// Apply drawable values to shader defaults

View File

@@ -193,7 +193,7 @@ gfx::AttributeBindingArray UploadPass::buildAttributeBindings(
const gfx::VertexAttributeArray& defaults,
const gfx::VertexAttributeArray& overrides,
const gfx::BufferUsageType usage,
const std::chrono::duration<double> lastUpdate,
const std::optional<std::chrono::duration<double>> lastUpdate,
/*out*/ std::vector<std::unique_ptr<gfx::VertexBufferResource>>& outBuffers) {
MLN_TRACE_FUNC();
AttributeBindingArray bindings;

View File

@@ -56,7 +56,7 @@ public:
const gfx::VertexAttributeArray& defaults,
const gfx::VertexAttributeArray& overrides,
gfx::BufferUsageType,
const std::chrono::duration<double> lastUpdate,
const std::optional<std::chrono::duration<double>> lastUpdate,
/*out*/ std::vector<std::unique_ptr<gfx::VertexBufferResource>>& outBuffers) override;
#endif

View File

@@ -155,11 +155,11 @@ std::size_t VertexAttributeGL::getStride() const {
const std::vector<std::uint8_t>& VertexAttributeGL::getRaw(gfx::VertexAttribute& attr,
platform::GLenum type,
std::chrono::duration<double> lastUpdate) {
std::optional<std::chrono::duration<double>> lastUpdate) {
const auto count = attr.getCount();
const auto stride_ = getStride(type);
auto& rawData = attr.getRawData();
if (attr.isModifiedAfter(lastUpdate) || rawData.size() != count * stride_) {
if (!lastUpdate || attr.isModifiedAfter(*lastUpdate) || rawData.size() != count * stride_) {
rawData.resize(stride_ * count);
if (!rawData.empty()) {

View File

@@ -241,13 +241,6 @@ bool Context::emplaceOrUpdateUniformBuffer(gfx::UniformBufferPtr& buffer,
}
}
const BufferResource& Context::getEmptyBuffer() {
if (!emptyBuffer) {
emptyBuffer.emplace(const_cast<Context&>(*this), nullptr, 0, MTL::ResourceStorageModeShared, false, false);
}
return *emptyBuffer;
}
const BufferResource& Context::getTileVertexBuffer() {
if (!tileVertexBuffer) {
const auto vertices = RenderStaticData::tileVertices();

View File

@@ -191,13 +191,13 @@ void Drawable::draw(PaintParameters& parameters) const {
bindUniformBuffers(renderPass);
bindTextures(renderPass);
if (!impl->indexes->getBuffer() || impl->indexes->isModifiedAfter(attributeUpdateTime)) {
if (!impl->indexes->getBuffer() || !attributeUpdateTime || impl->indexes->isModifiedAfter(*attributeUpdateTime)) {
assert(!"Index buffer not uploaded");
return;
}
const auto* indexBuffer = getMetalBuffer(impl->indexes);
if (!indexBuffer || impl->indexes->isModifiedAfter(attributeUpdateTime)) {
if (!indexBuffer || !attributeUpdateTime || impl->indexes->isModifiedAfter(*attributeUpdateTime)) {
assert(!"Index buffer not uploaded");
return;
}
@@ -341,6 +341,34 @@ void Drawable::setVertices(std::vector<uint8_t>&& data, std::size_t count, gfx::
}
}
void Drawable::updateVertexAttributes(gfx::VertexAttributeArrayPtr vertices,
std::size_t vertexCount,
gfx::DrawMode mode,
gfx::IndexVectorBasePtr indexes,
const SegmentBase* segments,
std::size_t segmentCount) {
gfx::Drawable::setVertexAttributes(std::move(vertices));
impl->vertexCount = vertexCount;
std::vector<UniqueDrawSegment> drawSegs;
drawSegs.reserve(segmentCount);
for (std::size_t i = 0; i < segmentCount; ++i) {
const auto& seg = segments[i];
auto segCopy = SegmentBase{
// no copy constructor
seg.vertexOffset,
seg.indexOffset,
seg.vertexLength,
seg.indexLength,
seg.sortKey,
};
drawSegs.push_back(std::make_unique<Drawable::DrawSegment>(mode, std::move(segCopy)));
}
impl->indexes = std::move(indexes);
impl->segments = std::move(drawSegs);
}
const gfx::UniformBufferArray& Drawable::getUniformBuffers() const {
return impl->uniformBuffers;
}
@@ -363,9 +391,6 @@ void Drawable::bindAttributes(RenderPass& renderPass) const noexcept {
if (buffer && buffer->get()) {
assert(binding->vertexStride * impl->vertexCount <= getBufferSize(binding->vertexBufferResource));
renderPass.bindVertex(buffer->get(), /*offset=*/0, attributeIndex);
} else {
auto& context = renderPass.getCommandEncoder().getContext();
renderPass.bindVertex(context.getEmptyBuffer(), /*offset=*/0, attributeIndex);
}
attributeIndex += 1;
}
@@ -380,9 +405,6 @@ void Drawable::bindInstanceAttributes(RenderPass& renderPass) const noexcept {
const auto* buffer = static_cast<const mtl::VertexBufferResource*>(binding->vertexBufferResource);
if (buffer && buffer->get()) {
renderPass.bindVertex(buffer->get(), /*offset=*/0, attributeIndex);
} else {
auto& context = renderPass.getCommandEncoder().getContext();
renderPass.bindVertex(context.getEmptyBuffer(), /*offset=*/0, attributeIndex);
}
}
attributeIndex += 1;
@@ -530,13 +552,13 @@ void Drawable::upload(gfx::UploadPass& uploadPass_) {
// We need either raw index data or a buffer already created from them.
// We can have a buffer and no indexes, but only if it's not marked dirty.
if (!impl->indexes || (impl->indexes->empty() &&
(!impl->indexes->getBuffer() || impl->indexes->isModifiedAfter(attributeUpdateTime)))) {
if (!impl->indexes || (impl->indexes->empty() && (!impl->indexes->getBuffer() || !attributeUpdateTime ||
impl->indexes->isModifiedAfter(*attributeUpdateTime)))) {
assert(!"Missing index data");
return;
}
if (!impl->indexes->getBuffer() || impl->indexes->isModifiedAfter(attributeUpdateTime)) {
if (!impl->indexes->getBuffer() || !attributeUpdateTime || impl->indexes->isModifiedAfter(*attributeUpdateTime)) {
// Create or update a buffer for the index data. We don't update any
// existing buffer because it may still be in use by the previous frame.
auto indexBufferResource{uploadPass.createIndexBufferResource(
@@ -548,8 +570,8 @@ void Drawable::upload(gfx::UploadPass& uploadPass_) {
impl->indexes->setBuffer(std::move(buffer));
}
const bool buildAttribs = !vertexAttributes || vertexAttributes->isModifiedAfter(attributeUpdateTime) ||
!impl->vertexDesc;
const bool buildAttribs = !impl->vertexDesc || !vertexAttributes || !attributeUpdateTime ||
vertexAttributes->isModifiedAfter(*attributeUpdateTime);
if (buildAttribs) {
#if !defined(NDEBUG)
@@ -630,7 +652,8 @@ void Drawable::upload(gfx::UploadPass& uploadPass_) {
}
// build instance buffer
const bool buildInstanceBuffer = (instanceAttributes && instanceAttributes->isModifiedAfter(attributeUpdateTime));
const bool buildInstanceBuffer =
(instanceAttributes && (!attributeUpdateTime || instanceAttributes->isModifiedAfter(*attributeUpdateTime)));
if (buildInstanceBuffer) {
// Build instance attribute buffers

View File

@@ -119,7 +119,8 @@ namespace {
const std::unique_ptr<gfx::VertexBufferResource> noBuffer;
}
const gfx::UniqueVertexBufferResource& UploadPass::getBuffer(const gfx::VertexVectorBasePtr& vec,
const gfx::BufferUsageType usage) {
const gfx::BufferUsageType usage,
bool forceUpdate) {
if (vec) {
const auto* rawBufPtr = vec->getRawData();
const auto rawBufSize = vec->getRawCount() * vec->getRawSize();
@@ -131,7 +132,7 @@ const gfx::UniqueVertexBufferResource& UploadPass::getBuffer(const gfx::VertexVe
// If the already-allocated buffer is large enough, we can re-use it
if (rawBufSize <= resource.getSizeInBytes()) {
// If the source changed, update the buffer contents
if (vec->isModifiedAfter(resource.getLastUpdated())) {
if (forceUpdate || vec->isModifiedAfter(resource.getLastUpdated())) {
updateVertexBufferResource(resource, rawBufPtr, rawBufSize);
resource.setLastUpdated(vec->getLastModified());
}
@@ -157,7 +158,7 @@ gfx::AttributeBindingArray UploadPass::buildAttributeBindings(
const gfx::VertexAttributeArray& defaults,
const gfx::VertexAttributeArray& overrides,
const gfx::BufferUsageType usage,
const std::chrono::duration<double> /*lastUpdate*/,
const std::optional<std::chrono::duration<double>> lastUpdate,
/*out*/ std::vector<std::unique_ptr<gfx::VertexBufferResource>>& outBuffers) {
MLN_TRACE_FUNC();
@@ -191,7 +192,7 @@ gfx::AttributeBindingArray UploadPass::buildAttributeBindings(
}
// If the attribute references data shared with a bucket, get the corresponding buffer.
if (const auto& buffer_ = getBuffer(effectiveAttr.getSharedRawData(), usage)) {
if (const auto& buffer_ = getBuffer(effectiveAttr.getSharedRawData(), usage, !lastUpdate)) {
assert(effectiveAttr.getSharedStride() * effectiveAttr.getSharedVertexOffset() <
effectiveAttr.getSharedRawData()->getRawSize() * effectiveAttr.getSharedRawData()->getRawCount());
@@ -207,7 +208,8 @@ gfx::AttributeBindingArray UploadPass::buildAttributeBindings(
assert(effectiveAttr.getStride() > 0);
// Otherwise, turn the data managed by the attribute into a buffer.
if (const auto& buffer_ = VertexAttribute::getBuffer(effectiveAttr, *this, gfx::BufferUsageType::StaticDraw)) {
if (const auto& buffer_ = VertexAttribute::getBuffer(
effectiveAttr, *this, gfx::BufferUsageType::StaticDraw, !lastUpdate)) {
bindings[index] = {
/*.attribute = */ {effectiveAttr.getDataType(), /*offset=*/0},
/*.vertexStride = */ static_cast<uint32_t>(effectiveAttr.getStride()),

View File

@@ -14,11 +14,12 @@ namespace mtl {
const gfx::UniqueVertexBufferResource& VertexAttribute::getBuffer(gfx::VertexAttribute& attrib_,
UploadPass& uploadPass,
const gfx::BufferUsageType usage) {
const gfx::BufferUsageType usage,
bool forceUpdate) {
if (!attrib_.getBuffer()) {
auto& attrib = static_cast<VertexAttribute&>(attrib_);
if (attrib.sharedRawData) {
return uploadPass.getBuffer(attrib.sharedRawData, usage);
return uploadPass.getBuffer(attrib.sharedRawData, usage, forceUpdate);
} else {
if (!attrib.rawData.empty()) {
auto buffer = uploadPass.createVertexBufferResource(

View File

@@ -56,6 +56,9 @@ void HillshadeBucket::clear() {
vertices.clear();
indices.clear();
vertices.updateModified();
indices.updateModified();
uploaded = false;
}
@@ -116,6 +119,9 @@ void HillshadeBucket::setMask(TileMask&& mask_) {
segment.vertexLength += vertexLength;
segment.indexLength += 6;
}
vertices.updateModified();
indices.updateModified();
}
bool HillshadeBucket::hasData() const {

View File

@@ -49,7 +49,10 @@ public:
std::shared_ptr<VertexVector> sharedVertices = std::make_shared<VertexVector>();
VertexVector& vertices = *sharedVertices;
gfx::IndexVector<gfx::Triangles> indices;
using IndexVector = gfx::IndexVector<gfx::Triangles>;
std::shared_ptr<IndexVector> sharedIndices = std::make_shared<IndexVector>();
IndexVector& indices = *sharedIndices;
SegmentVector<HillshadeAttributes> segments;
#if MLN_LEGACY_RENDERER

View File

@@ -46,6 +46,9 @@ void RasterBucket::clear() {
vertices.clear();
indices.clear();
vertices.updateModified();
indices.updateModified();
uploaded = false;
}

View File

@@ -351,6 +351,7 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
const auto layerPrefix = getID() + "/";
constexpr auto renderPass = RenderPass::Translucent;
constexpr auto lineWidth = 2.0f;
const auto commonInit = [&](gfx::DrawableBuilder& builder) {
builder.setCullFaceMode(gfx::CullFaceMode::disabled());
@@ -415,7 +416,7 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
vertexAttrs->readDataDrivenPaintProperties<FillColor, FillOpacity, FillOutlineColor, FillPattern>(
binders, evaluated, propertiesAsUniforms, idFillColorVertexAttribute);
const auto vertexCount = bucket.vertices.elements();
const auto fillVertexCount = bucket.vertices.elements();
if (const auto& attr = vertexAttrs->set(idFillPosVertexAttribute)) {
attr->setSharedRawData(bucket.sharedVertices,
offsetof(FillLayoutVertex, a1),
@@ -424,27 +425,91 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
gfx::AttributeDataType::Short2);
}
#if MLN_TRIANGULATE_FILL_OUTLINES
const auto lineVertexCount = bucket.lineVertices.elements();
const auto getTriangulatedAttributes = [&]() {
auto attrs = context.createVertexAttributeArray();
if (const auto& attr = attrs->set(idLinePosNormalVertexAttribute)) {
attr->setSharedRawData(bucket.sharedLineVertices,
offsetof(LineLayoutVertex, a1),
/*vertexOffset=*/0,
sizeof(LineLayoutVertex),
gfx::AttributeDataType::Short2);
}
if (const auto& attr = attrs->set(idLineDataVertexAttribute)) {
attr->setSharedRawData(bucket.sharedLineVertices,
offsetof(LineLayoutVertex, a2),
/*vertexOffset=*/0,
sizeof(LineLayoutVertex),
gfx::AttributeDataType::UByte4);
}
return attrs;
};
#endif
// If we already have drawables for this tile, update them.
auto updateExisting = [&](gfx::Drawable& drawable) {
if (drawable.getLayerTweaker() != layerTweaker) {
// This drawable was produced on a previous style/bucket, and should not be updated.
return false;
}
drawable.setVertexAttributes(vertexAttrs);
switch (static_cast<FillVariant>(drawable.getType())) {
case FillVariant::Fill:
case FillVariant::FillPattern:
drawable.updateVertexAttributes(vertexAttrs,
fillVertexCount,
gfx::Triangles(),
bucket.sharedTriangles,
bucket.triangleSegments.data(),
bucket.triangleSegments.size());
break;
case FillVariant::FillOutline:
case FillVariant::FillOutlinePattern:
drawable.updateVertexAttributes(vertexAttrs,
fillVertexCount,
gfx::Lines(lineWidth),
bucket.sharedBasicLineIndexes,
bucket.basicLineSegments.data(),
bucket.basicLineSegments.size());
break;
#if MLN_TRIANGULATE_FILL_OUTLINES
case FillVariant::FillOutlineTriangulated:
if (const auto updated = drawable.getAttributeUpdateTime();
!updated || bucket.lineVertices.getLastModified() > *updated) {
drawable.updateVertexAttributes(getTriangulatedAttributes(),
lineVertexCount,
gfx::Triangles(),
bucket.sharedLineIndexes,
bucket.lineSegments.data(),
bucket.lineSegments.size());
}
break;
#endif
default:
Log::Error(Event::General, "Invalid fill type " + util::toString(drawable.getType()));
assert(false);
return false;
}
return true;
};
if (updateTile(renderPass, tileID, std::move(updateExisting))) {
continue;
}
const auto addDrawable = [&](std::unique_ptr<gfx::Drawable> drawable, FillVariant type) {
drawable->setTileID(tileID);
drawable->setType(static_cast<size_t>(type));
drawable->setLayerTweaker(layerTweaker);
drawable->setBinders(renderData->bucket, &binders);
drawable->setRenderTile(renderTilesOwner, &tile);
fillTileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable));
++stats.drawablesAdded;
const auto finish = [&](gfx::DrawableBuilder& builder, FillVariant type) {
builder.flush(context);
for (auto& drawable : builder.clearDrawables()) {
drawable->setTileID(tileID);
drawable->setType(static_cast<size_t>(type));
drawable->setLayerTweaker(layerTweaker);
drawable->setBinders(renderData->bucket, &binders);
drawable->setRenderTile(renderTilesOwner, &tile);
fillTileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable));
++stats.drawablesAdded;
}
};
// Outline always occurs in translucent pass, defaults to fill color
@@ -475,36 +540,15 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
: nullptr;
auto createOutlineTriangulated = [&](auto& builder) {
if (doOutline && builder && bucket.sharedLineIndexes->elements()) {
if (doOutline && builder && lineVertexCount) {
builder->setShader(outlineTriangulatedShader);
builder->setRawVertices({}, bucket.lineVertices.elements(), gfx::AttributeDataType::Short2);
auto attrs = context.createVertexAttributeArray();
if (const auto& attr = attrs->set(idLinePosNormalVertexAttribute)) {
attr->setSharedRawData(bucket.sharedLineVertices,
offsetof(LineLayoutVertex, a1),
/*vertexOffset=*/0,
sizeof(LineLayoutVertex),
gfx::AttributeDataType::Short2);
}
if (const auto& attr = attrs->set(idLineDataVertexAttribute)) {
attr->setSharedRawData(bucket.sharedLineVertices,
offsetof(LineLayoutVertex, a2),
/*vertexOffset=*/0,
sizeof(LineLayoutVertex),
gfx::AttributeDataType::UByte4);
}
builder->setVertexAttributes(std::move(attrs));
builder->setRawVertices({}, lineVertexCount, gfx::AttributeDataType::Short2);
builder->setVertexAttributes(getTriangulatedAttributes());
builder->setSegments(gfx::Triangles(),
bucket.sharedLineIndexes,
bucket.lineSegments.data(),
bucket.lineSegments.size());
builder->flush(context);
for (auto& drawable : builder->clearDrawables()) {
addDrawable(std::move(drawable), FillVariant::FillOutlineTriangulated);
}
finish(*builder, FillVariant::FillOutlineTriangulated);
}
};
#endif
@@ -531,7 +575,7 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
if (auto builder = context.createDrawableBuilder(layerPrefix + "fill-outline")) {
commonInit(*builder);
builder->setDepthType(gfx::DepthMaskType::ReadOnly);
builder->setLineWidth(2.0f);
builder->setLineWidth(lineWidth);
builder->setSubLayerIndex(unevaluated.get<FillOutlineColor>().isUndefined() ? 2 : 0);
builder->setColorMode(gfx::ColorMode::alphaBlended());
builder->setRenderPass(RenderPass::Translucent);
@@ -539,32 +583,20 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
}
}
const auto finish = [&](gfx::DrawableBuilder& builder, FillVariant type) {
builder.flush(context);
for (auto& drawable : builder.clearDrawables()) {
addDrawable(std::move(drawable), type);
}
};
if (fillBuilder && bucket.sharedTriangles->elements()) {
fillBuilder->setShader(fillShader);
#if MLN_TRIANGULATE_FILL_OUTLINES
if (doOutline && dataDrivenOutline && outlineBuilder) {
fillBuilder->setVertexAttributes(vertexAttrs);
outlineBuilder->setVertexAttributes(std::move(vertexAttrs));
} else {
fillBuilder->setVertexAttributes(std::move(vertexAttrs));
outlineBuilder->setVertexAttributes(vertexAttrs);
}
#else
if (doOutline && outlineBuilder) {
fillBuilder->setVertexAttributes(vertexAttrs);
outlineBuilder->setVertexAttributes(std::move(vertexAttrs));
} else {
fillBuilder->setVertexAttributes(std::move(vertexAttrs));
outlineBuilder->setVertexAttributes(vertexAttrs);
}
#endif
fillBuilder->setRawVertices({}, vertexCount, gfx::AttributeDataType::Short2);
fillBuilder->setVertexAttributes(std::move(vertexAttrs));
fillBuilder->setRawVertices({}, fillVertexCount, gfx::AttributeDataType::Short2);
fillBuilder->setSegments(gfx::Triangles(),
bucket.sharedTriangles,
bucket.triangleSegments.data(),
@@ -580,8 +612,8 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
} else {
if (bucket.sharedBasicLineIndexes->elements()) {
outlineBuilder->setShader(outlineShader);
outlineBuilder->setRawVertices({}, vertexCount, gfx::AttributeDataType::Short2);
outlineBuilder->setSegments(gfx::Lines(2),
outlineBuilder->setRawVertices({}, fillVertexCount, gfx::AttributeDataType::Short2);
outlineBuilder->setSegments(gfx::Lines(lineWidth),
bucket.sharedBasicLineIndexes,
bucket.basicLineSegments.data(),
bucket.basicLineSegments.size());
@@ -592,8 +624,8 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
#else
if (doOutline && outlineBuilder && bucket.sharedBasicLineIndexes->elements()) {
outlineBuilder->setShader(outlineShader);
outlineBuilder->setRawVertices({}, vertexCount, gfx::AttributeDataType::Short2);
outlineBuilder->setSegments(gfx::Lines(2),
outlineBuilder->setRawVertices({}, fillVertexCount, gfx::AttributeDataType::Short2);
outlineBuilder->setSegments(gfx::Lines(lineWidth),
bucket.sharedBasicLineIndexes,
bucket.basicLineSegments.data(),
bucket.basicLineSegments.size());
@@ -632,7 +664,7 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
if (auto builder = context.createDrawableBuilder(layerPrefix + "fill-outline-pattern")) {
commonInit(*builder);
builder->setShader(outlineShader);
builder->setLineWidth(2.0f);
builder->setLineWidth(lineWidth);
builder->setDepthType(gfx::DepthMaskType::ReadOnly);
builder->setColorMode(gfx::ColorMode::alphaBlended());
builder->setSubLayerIndex(2);
@@ -650,24 +682,14 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
outlinePatternBuilder->addTweaker(getAtlasTweaker());
}
const auto finish = [&](gfx::DrawableBuilder& builder, FillVariant type) {
builder.flush(context);
for (auto& drawable : builder.clearDrawables()) {
addDrawable(std::move(drawable), type);
}
};
if (patternBuilder && bucket.sharedTriangles->elements()) {
patternBuilder->setShader(fillShader);
patternBuilder->setRenderPass(renderPass);
if (doOutline && outlinePatternBuilder) {
patternBuilder->setVertexAttributes(vertexAttrs);
outlinePatternBuilder->setVertexAttributes(std::move(vertexAttrs));
} else {
patternBuilder->setVertexAttributes(std::move(vertexAttrs));
outlinePatternBuilder->setVertexAttributes(vertexAttrs);
}
patternBuilder->setRawVertices({}, vertexCount, gfx::AttributeDataType::Short2);
patternBuilder->setVertexAttributes(std::move(vertexAttrs));
patternBuilder->setRawVertices({}, fillVertexCount, gfx::AttributeDataType::Short2);
patternBuilder->setSegments(gfx::Triangles(),
bucket.sharedTriangles,
bucket.triangleSegments.data(),
@@ -679,8 +701,8 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
if (doOutline && outlinePatternBuilder && bucket.sharedBasicLineIndexes->elements()) {
outlinePatternBuilder->setShader(outlineShader);
outlinePatternBuilder->setRenderPass(renderPass);
outlinePatternBuilder->setRawVertices({}, vertexCount, gfx::AttributeDataType::Short2);
outlinePatternBuilder->setSegments(gfx::Lines(2),
outlinePatternBuilder->setRawVertices({}, fillVertexCount, gfx::AttributeDataType::Short2);
outlinePatternBuilder->setSegments(gfx::Lines(lineWidth),
bucket.sharedBasicLineIndexes,
bucket.basicLineSegments.data(),
bucket.basicLineSegments.size());

View File

@@ -450,17 +450,22 @@ void RenderHillshadeLayer::update(gfx::ShaderRegistry& shaders,
}
// Set up tile drawable
auto vertices = staticDataSharedVertices;
auto* indices = &staticDataIndices;
std::shared_ptr<HillshadeVertexVector> vertices;
std::shared_ptr<gfx::IndexVector<gfx::Triangles>> indices;
auto* segments = &staticDataSegments;
if (!bucket.vertices.empty() && !bucket.indices.empty() && !bucket.segments.empty()) {
vertices = bucket.sharedVertices;
indices = &bucket.indices;
indices = bucket.sharedIndices;
segments = &bucket.segments;
} else {
vertices = staticDataSharedVertices;
indices = std::make_shared<gfx::IndexVector<gfx::Triangles>>(staticDataIndices);
}
hillshadeBuilder = context.createDrawableBuilder("hillshade");
if (!hillshadeBuilder) {
hillshadeBuilder = context.createDrawableBuilder("hillshade");
}
gfx::VertexAttributeArrayPtr hillshadeVertexAttrs;
auto buildVertexAttributes = [&] {
@@ -492,25 +497,14 @@ void RenderHillshadeLayer::update(gfx::ShaderRegistry& shaders,
return false;
}
drawable.setVertexAttributes(buildVertexAttributes());
drawable.setVertices({}, vertices->elements(), gfx::AttributeDataType::Short2);
// Rebuild segments, since they're not shared.
std::vector<std::unique_ptr<gfx::Drawable::DrawSegment>> drawSegments;
drawSegments.reserve(segments->size());
for (const auto& seg : *segments) {
auto segCopy = SegmentBase{
// no copy constructor
seg.vertexOffset,
seg.indexOffset,
seg.vertexLength,
seg.indexLength,
seg.sortKey,
};
drawSegments.emplace_back(hillshadeBuilder->createSegment(gfx::Triangles(), std::move(segCopy)));
}
drawable.setIndexData(indices->vector(), std::move(drawSegments));
drawable.updateVertexAttributes(buildVertexAttributes(),
vertices->elements(),
gfx::Triangles(),
std::move(indices),
segments->data(),
segments->size());
drawable.setTexture(bucket.renderTarget->getTexture(), idHillshadeImageTexture);
return true;
};
if (updateTile(renderPass, tileID, std::move(updateExisting))) {

View File

@@ -377,7 +377,8 @@ void RenderRasterLayer::update(gfx::ShaderRegistry& shaders,
assert(!!drawable ^ !!builder);
if (drawable) {
drawable->setVertexAttributes(vertexAttrs);
drawable->updateVertexAttributes(
vertexAttrs, vertices->elements(), gfx::Triangles(), indices, segments->data(), segments->size());
} else if (builder) {
builder->setVertexAttributes(vertexAttrs);
builder->setRawVertices({}, vertices->elements(), gfx::AttributeDataType::Short2);

View File

@@ -100,6 +100,34 @@ void Drawable::setCullFaceMode(const gfx::CullFaceMode& mode) {
impl->pipelineInfo.setCullMode(mode);
}
void Drawable::updateVertexAttributes(gfx::VertexAttributeArrayPtr vertices,
std::size_t vertexCount,
gfx::DrawMode mode,
gfx::IndexVectorBasePtr indexes,
const SegmentBase* segments,
std::size_t segmentCount) {
gfx::Drawable::setVertexAttributes(std::move(vertices));
impl->vertexCount = vertexCount;
std::vector<std::unique_ptr<Drawable::DrawSegment>> drawSegs;
drawSegs.reserve(segmentCount);
for (std::size_t i = 0; i < segmentCount; ++i) {
const auto& seg = segments[i];
auto segCopy = SegmentBase{
// no copy constructor
seg.vertexOffset,
seg.indexOffset,
seg.vertexLength,
seg.indexLength,
seg.sortKey,
};
drawSegs.push_back(std::make_unique<Drawable::DrawSegment>(mode, std::move(segCopy)));
}
impl->indexes = std::move(indexes);
impl->segments = std::move(drawSegs);
}
void Drawable::upload(gfx::UploadPass& uploadPass_) {
if (isCustom) {
return;
@@ -116,13 +144,13 @@ void Drawable::upload(gfx::UploadPass& uploadPass_) {
// We need either raw index data or a buffer already created from them.
// We can have a buffer and no indexes, but only if it's not marked dirty.
if (!impl->indexes || (impl->indexes->empty() &&
(!impl->indexes->getBuffer() || impl->indexes->isModifiedAfter(attributeUpdateTime)))) {
if (!impl->indexes || (impl->indexes->empty() && (!impl->indexes->getBuffer() || !attributeUpdateTime ||
impl->indexes->isModifiedAfter(*attributeUpdateTime)))) {
assert(!"Missing index data");
return;
}
if (!impl->indexes->getBuffer() || impl->indexes->isModifiedAfter(attributeUpdateTime)) {
if (!impl->indexes->getBuffer() || !attributeUpdateTime || impl->indexes->isModifiedAfter(*attributeUpdateTime)) {
// Create a buffer for the index data. We don't update any
// existing buffer because it may still be in use by the previous frame.
auto indexBufferResource{uploadPass.createIndexBufferResource(
@@ -134,7 +162,8 @@ void Drawable::upload(gfx::UploadPass& uploadPass_) {
impl->indexes->setBuffer(std::move(buffer));
}
const bool buildAttribs = !vertexAttributes || vertexAttributes->isModifiedAfter(attributeUpdateTime) ||
const bool buildAttribs = !vertexAttributes || !attributeUpdateTime ||
vertexAttributes->isModifiedAfter(*attributeUpdateTime) ||
impl->pipelineInfo.inputAttributes.empty();
if (buildAttribs) {
@@ -166,7 +195,8 @@ void Drawable::upload(gfx::UploadPass& uploadPass_) {
}
// build instance buffer
const bool buildInstanceBuffer = (instanceAttributes && instanceAttributes->isModifiedAfter(attributeUpdateTime));
const bool buildInstanceBuffer =
(instanceAttributes && (!attributeUpdateTime || instanceAttributes->isModifiedAfter(*attributeUpdateTime)));
if (buildInstanceBuffer) {
// Build instance attribute buffers

View File

@@ -84,7 +84,8 @@ struct VertexBuffer : public gfx::VertexBufferBase {
static const std::unique_ptr<gfx::VertexBufferResource> noBuffer;
const gfx::UniqueVertexBufferResource& UploadPass::getBuffer(const gfx::VertexVectorBasePtr& vec,
const gfx::BufferUsageType usage) {
const gfx::BufferUsageType usage,
bool forceUpdate) {
if (vec) {
const auto* rawBufPtr = vec->getRawData();
const auto rawBufSize = vec->getRawCount() * vec->getRawSize();
@@ -95,7 +96,7 @@ const gfx::UniqueVertexBufferResource& UploadPass::getBuffer(const gfx::VertexVe
// If it's changed, update it
if (rawBufSize <= resource.getSizeInBytes()) {
if (vec->isModifiedAfter(resource.getLastUpdated())) {
if (forceUpdate || vec->isModifiedAfter(resource.getLastUpdated())) {
updateVertexBufferResource(resource, rawBufPtr, rawBufSize);
resource.setLastUpdated(vec->getLastModified());
}
@@ -121,7 +122,7 @@ gfx::AttributeBindingArray UploadPass::buildAttributeBindings(
const gfx::VertexAttributeArray& defaults,
const gfx::VertexAttributeArray& overrides,
const gfx::BufferUsageType usage,
const std::chrono::duration<double> /*lastUpdate*/,
const std::optional<std::chrono::duration<double>> lastUpdate,
/*out*/ std::vector<std::unique_ptr<gfx::VertexBufferResource>>&) {
gfx::AttributeBindingArray bindings;
@@ -147,7 +148,7 @@ gfx::AttributeBindingArray UploadPass::buildAttributeBindings(
}
// If the attribute references data shared with a bucket, get the corresponding buffer.
if (const auto& buffer = getBuffer(effectiveAttr.getSharedRawData(), usage)) {
if (const auto& buffer = getBuffer(effectiveAttr.getSharedRawData(), usage, !lastUpdate)) {
assert(effectiveAttr.getSharedStride() * effectiveAttr.getSharedVertexOffset() <
effectiveAttr.getSharedRawData()->getRawSize() * effectiveAttr.getSharedRawData()->getRawCount());
@@ -163,7 +164,8 @@ gfx::AttributeBindingArray UploadPass::buildAttributeBindings(
assert(effectiveAttr.getStride() > 0);
// Otherwise, turn the data managed by the attribute into a buffer.
if (const auto& buffer = VertexAttribute::getBuffer(effectiveAttr, *this, gfx::BufferUsageType::StaticDraw)) {
if (const auto& buffer = VertexAttribute::getBuffer(
effectiveAttr, *this, gfx::BufferUsageType::StaticDraw, !lastUpdate)) {
bindings[index] = {
/*.attribute = */ {effectiveAttr.getDataType(), /*offset=*/0},
/*.vertexStride = */ static_cast<uint32_t>(effectiveAttr.getStride()),

View File

@@ -14,11 +14,12 @@ namespace vulkan {
const gfx::UniqueVertexBufferResource& VertexAttribute::getBuffer(gfx::VertexAttribute& attrib_,
UploadPass& uploadPass,
const gfx::BufferUsageType usage) {
const gfx::BufferUsageType usage,
bool forceUpdate) {
if (!attrib_.getBuffer()) {
auto& attrib = static_cast<VertexAttribute&>(attrib_);
if (attrib.sharedRawData) {
return uploadPass.getBuffer(attrib.sharedRawData, usage);
return uploadPass.getBuffer(attrib.sharedRawData, usage, forceUpdate);
} else {
if (!attrib.rawData.empty()) {
auto buffer = uploadPass.createVertexBufferResource(