diff options
author | Dave Parks <davep@lindenlab.com> | 2024-06-21 13:13:08 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-21 13:13:08 -0500 |
commit | 80ea30af1a8b38360f71c29cc45872c4399dab0d (patch) | |
tree | db07a706a4c896b11b8abca8a7d8815797a8d0be /indra/newview/gltf | |
parent | 9fb9e8f33cb33a1535f43b4be030009c192ea92b (diff) |
#1769 gltf optimization pass (#1816)
#1814 and #1517 Fix mirror update rate and occlusion culling
Diffstat (limited to 'indra/newview/gltf')
-rw-r--r-- | indra/newview/gltf/animation.cpp | 98 | ||||
-rw-r--r-- | indra/newview/gltf/animation.h | 3 | ||||
-rw-r--r-- | indra/newview/gltf/asset.cpp | 299 | ||||
-rw-r--r-- | indra/newview/gltf/asset.h | 127 | ||||
-rw-r--r-- | indra/newview/gltf/common.h | 14 | ||||
-rw-r--r-- | indra/newview/gltf/primitive.cpp | 90 | ||||
-rw-r--r-- | indra/newview/gltf/primitive.h | 21 |
7 files changed, 489 insertions, 163 deletions
diff --git a/indra/newview/gltf/animation.cpp b/indra/newview/gltf/animation.cpp index 8b85eba3e5..3dff67d746 100644 --- a/indra/newview/gltf/animation.cpp +++ b/indra/newview/gltf/animation.cpp @@ -70,6 +70,14 @@ bool Animation::prep(Asset& asset) } } + for (auto& channel : mScaleChannels) + { + if (!channel.prep(asset, mSamplers[channel.mSampler])) + { + return false; + } + } + return true; } @@ -82,18 +90,37 @@ void Animation::update(Asset& asset, F32 dt) void Animation::apply(Asset& asset, float time) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; + // convert time to animation loop time time = fmod(time, mMaxTime - mMinTime) + mMinTime; // apply each channel - for (auto& channel : mRotationChannels) { - channel.apply(asset, mSamplers[channel.mSampler], time); + LL_PROFILE_ZONE_NAMED_CATEGORY_GLTF("gltfanim - rotation"); + + for (auto& channel : mRotationChannels) + { + channel.apply(asset, mSamplers[channel.mSampler], time); + } + } + + { + LL_PROFILE_ZONE_NAMED_CATEGORY_GLTF("gltfanim - translation"); + + for (auto& channel : mTranslationChannels) + { + channel.apply(asset, mSamplers[channel.mSampler], time); + } } - for (auto& channel : mTranslationChannels) { - channel.apply(asset, mSamplers[channel.mSampler], time); + LL_PROFILE_ZONE_NAMED_CATEGORY_GLTF("gltfanim - scale"); + + for (auto& channel : mScaleChannels) + { + channel.apply(asset, mSamplers[channel.mSampler], time); + } } }; @@ -178,7 +205,8 @@ const Animation::Channel& Animation::Channel::operator=(const Value& src) void Animation::Sampler::getFrameInfo(Asset& asset, F32 time, U32& frameIndex, F32& t) { - LL_PROFILE_ZONE_SCOPED; + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; + llassert(mFrameTimes.size() > 1); // if there is only one frame, there is no need to interpolate if (time < mMinTime) { @@ -187,32 +215,33 @@ void Animation::Sampler::getFrameInfo(Asset& asset, F32 time, U32& frameIndex, F return; } - if (mFrameTimes.size() > 1) + frameIndex = U32(mFrameTimes.size()) - 2; + t = 1.f; + + if (time > mMaxTime) { - llassert(mFrameTimes.size() <= size_t(U32_MAX)); - frameIndex = U32(mFrameTimes.size()) - 2; - t = 1.f; + return; + } - if (time > mMaxTime) - { - return; - } + if (time < mLastFrameTime) + { + mLastFrameIndex = 0; + } - for (U32 i = 0; i < (U32)mFrameTimes.size() - 1; i++) + mLastFrameTime = time; + + U32 idx = mLastFrameIndex; + + for (U32 i = idx; i < (U32)mFrameTimes.size() - 1; i++) + { + if (time >= mFrameTimes[i] && time < mFrameTimes[i + 1]) { - if (time >= mFrameTimes[i] && time < mFrameTimes[i + 1]) - { - frameIndex = i; - t = (time - mFrameTimes[i]) / (mFrameTimes[i + 1] - mFrameTimes[i]); - return; - } + frameIndex = i; + t = (time - mFrameTimes[i]) / (mFrameTimes[i + 1] - mFrameTimes[i]); + mLastFrameIndex = frameIndex; + return; } } - else - { - frameIndex = 0; - t = 0.0f; - } } bool Animation::RotationChannel::prep(Asset& asset, Animation::Sampler& sampler) @@ -231,14 +260,14 @@ void Animation::RotationChannel::apply(Asset& asset, Sampler& sampler, F32 time) Node& node = asset.mNodes[mTarget.mNode]; - sampler.getFrameInfo(asset, time, frameIndex, t); - - if (sampler.mFrameTimes.size() == 1) + if (sampler.mFrameTimes.size() < 2) { node.setRotation(mRotations[0]); } else { + sampler.getFrameInfo(asset, time, frameIndex, t); + // interpolate quat qf = glm::slerp(mRotations[frameIndex], mRotations[frameIndex + 1], t); @@ -264,14 +293,14 @@ void Animation::TranslationChannel::apply(Asset& asset, Sampler& sampler, F32 ti Node& node = asset.mNodes[mTarget.mNode]; - sampler.getFrameInfo(asset, time, frameIndex, t); - - if (sampler.mFrameTimes.size() == 1) + if (sampler.mFrameTimes.size() < 2) { node.setTranslation(mTranslations[0]); } else { + sampler.getFrameInfo(asset, time, frameIndex, t); + // interpolate const vec3& v0 = mTranslations[frameIndex]; const vec3& v1 = mTranslations[frameIndex + 1]; @@ -298,14 +327,14 @@ void Animation::ScaleChannel::apply(Asset& asset, Sampler& sampler, F32 time) Node& node = asset.mNodes[mTarget.mNode]; - sampler.getFrameInfo(asset, time, frameIndex, t); - - if (sampler.mFrameTimes.size() == 1) + if (sampler.mFrameTimes.size() < 2) { node.setScale(mScales[0]); } else { + sampler.getFrameInfo(asset, time, frameIndex, t); + // interpolate const vec3& v0 = mScales[frameIndex]; const vec3& v1 = mScales[frameIndex + 1]; @@ -373,6 +402,7 @@ Skin::~Skin() void Skin::uploadMatrixPalette(Asset& asset) { // prepare matrix palette + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; U32 max_joints = LLSkinningUtil::getMaxGLTFJointCount(); diff --git a/indra/newview/gltf/animation.h b/indra/newview/gltf/animation.h index d5426fd4ce..ab8839470a 100644 --- a/indra/newview/gltf/animation.h +++ b/indra/newview/gltf/animation.h @@ -49,6 +49,9 @@ namespace LL S32 mOutput = INVALID_INDEX; std::string mInterpolation; + F32 mLastFrameTime = 0.f; + U32 mLastFrameIndex = 0; + bool prep(Asset& asset); void serialize(boost::json::object& dst) const; diff --git a/indra/newview/gltf/asset.cpp b/indra/newview/gltf/asset.cpp index 21be69aae2..a454e68a92 100644 --- a/indra/newview/gltf/asset.cpp +++ b/indra/newview/gltf/asset.cpp @@ -35,6 +35,7 @@ #include "buffer_util.h" #include <boost/url.hpp> #include "llimagejpeg.h" +#include "../llskinningutil.h" using namespace LL::GLTF; using namespace boost::json; @@ -86,7 +87,6 @@ namespace LL } } - void Scene::updateTransforms(Asset& asset) { mat4 identity = glm::identity<mat4>(); @@ -98,26 +98,6 @@ void Scene::updateTransforms(Asset& asset) } } -void Scene::updateRenderTransforms(Asset& asset, const mat4& modelview) -{ - for (auto& nodeIndex : mNodes) - { - Node& node = asset.mNodes[nodeIndex]; - node.updateRenderTransforms(asset, modelview); - } -} - -void Node::updateRenderTransforms(Asset& asset, const mat4& modelview) -{ - mRenderMatrix = modelview * mMatrix; - - for (auto& childIndex : mChildren) - { - Node& child = asset.mNodes[childIndex]; - child.updateRenderTransforms(asset, mRenderMatrix); - } -} - void Node::updateTransforms(Asset& asset, const mat4& parentMatrix) { makeMatrixValid(); @@ -137,19 +117,119 @@ void Node::updateTransforms(Asset& asset, const mat4& parentMatrix) void Asset::updateTransforms() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; for (auto& scene : mScenes) { scene.updateTransforms(*this); } + + uploadTransforms(); } -void Asset::updateRenderTransforms(const mat4& modelview) +void Asset::uploadTransforms() { - // use mAssetMatrix to update render transforms from node list - for (auto& node : mNodes) + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; + // prepare matrix palette + U32 max_nodes = LLSkinningUtil::getMaxGLTFJointCount(); + + size_t node_count = llmin<size_t>(max_nodes, mNodes.size()); + + std::vector<mat4> t_mp; + + t_mp.resize(node_count); + + for (U32 i = 0; i < node_count; ++i) { - node.mRenderMatrix = modelview * node.mAssetMatrix; + Node& node = mNodes[i]; + // build matrix palette in asset space + t_mp[i] = node.mAssetMatrix; } + + std::vector<F32> glmp; + + glmp.resize(node_count * 12); + + F32* mp = glmp.data(); + + for (U32 i = 0; i < node_count; ++i) + { + F32* m = glm::value_ptr(t_mp[i]); + + U32 idx = i * 12; + + mp[idx + 0] = m[0]; + mp[idx + 1] = m[1]; + mp[idx + 2] = m[2]; + mp[idx + 3] = m[12]; + + mp[idx + 4] = m[4]; + mp[idx + 5] = m[5]; + mp[idx + 6] = m[6]; + mp[idx + 7] = m[13]; + + mp[idx + 8] = m[8]; + mp[idx + 9] = m[9]; + mp[idx + 10] = m[10]; + mp[idx + 11] = m[14]; + } + + if (mNodesUBO == 0) + { + glGenBuffers(1, &mNodesUBO); + } + + glBindBuffer(GL_UNIFORM_BUFFER, mNodesUBO); + glBufferData(GL_UNIFORM_BUFFER, glmp.size() * sizeof(F32), glmp.data(), GL_STREAM_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); +} + +void Asset::uploadMaterials() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; + // see pbrmetallicroughnessV.glsl for the layout of the material UBO + std::vector<vec4> md; + + U32 material_size = sizeof(vec4) * 12; + U32 max_materials = gGLManager.mMaxUniformBlockSize / material_size; + + U32 mat_count = (U32)mMaterials.size(); + mat_count = llmin(mat_count, max_materials); + + md.resize(mat_count * 12); + + for (U32 i = 0; i < mat_count*12; i += 12) + { + Material& material = mMaterials[i/12]; + + // add texture transforms and UV indices + material.mPbrMetallicRoughness.mBaseColorTexture.mTextureTransform.getPacked(&md[i+0]); + md[i + 1].g = (F32)material.mPbrMetallicRoughness.mBaseColorTexture.getTexCoord(); + material.mNormalTexture.mTextureTransform.getPacked(&md[i + 2]); + md[i + 3].g = (F32)material.mNormalTexture.getTexCoord(); + material.mPbrMetallicRoughness.mMetallicRoughnessTexture.mTextureTransform.getPacked(&md[i+4]); + md[i + 5].g = (F32)material.mPbrMetallicRoughness.mMetallicRoughnessTexture.getTexCoord(); + material.mEmissiveTexture.mTextureTransform.getPacked(&md[i + 6]); + md[i + 7].g = (F32)material.mEmissiveTexture.getTexCoord(); + material.mOcclusionTexture.mTextureTransform.getPacked(&md[i + 8]); + md[i + 9].g = (F32)material.mOcclusionTexture.getTexCoord(); + + // add material properties + F32 min_alpha = material.mAlphaMode == Material::AlphaMode::MASK ? material.mAlphaCutoff : -1.0f; + md[i + 10] = vec4(material.mEmissiveFactor, 1.f); + md[i + 11] = vec4(0.f, + material.mPbrMetallicRoughness.mRoughnessFactor, + material.mPbrMetallicRoughness.mMetallicFactor, + min_alpha); + } + + if (mMaterialsUBO == 0) + { + glGenBuffers(1, &mMaterialsUBO); + } + + glBindBuffer(GL_UNIFORM_BUFFER, mMaterialsUBO); + glBufferData(GL_UNIFORM_BUFFER, md.size() * sizeof(vec4), md.data(), GL_STREAM_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); } S32 Asset::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, @@ -363,6 +443,7 @@ const Image& Image::operator=(const Value& src) void Asset::update() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; F32 dt = gFrameTimeSeconds - mLastUpdateTime; if (dt > 0.f) @@ -383,11 +464,27 @@ void Asset::update() { skin.uploadMatrixPalette(*this); } + + uploadMaterials(); + + { + LL_PROFILE_ZONE_NAMED_CATEGORY_GLTF("gltf - addTextureStats"); + + for (auto& image : mImages) + { + if (image.mTexture.notNull()) + { // HACK - force texture to be loaded full rez + // TODO: calculate actual vsize + image.mTexture->addTextureStats(2048.f * 2048.f); + } + } + } } } bool Asset::prep() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; // check required extensions and fail if not supported bool unsupported = false; for (auto& extension : mExtensionsRequired) @@ -445,6 +542,127 @@ bool Asset::prep() } } + // prepare vertex buffers + + // material count is number of materials + 1 for default material + U32 mat_count = (U32) mMaterials.size() + 1; + + if (LLGLSLShader::sCurBoundShaderPtr == nullptr) + { // make sure a shader is bound to satisfy mVertexBuffer->setBuffer + gDebugProgram.bind(); + } + + for (S32 double_sided = 0; double_sided < 2; ++double_sided) + { + RenderData& rd = mRenderData[double_sided]; + for (U32 i = 0; i < LLGLSLShader::NUM_GLTF_VARIANTS; ++i) + { + rd.mBatches[i].resize(mat_count); + } + + // for each material + for (S32 mat_id = -1; mat_id < (S32)mMaterials.size(); ++mat_id) + { + // for each shader variant + U32 vertex_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 }; + U32 index_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 }; + + S32 ds_mat = mat_id == -1 ? 0 : mMaterials[mat_id].mDoubleSided; + if (ds_mat != double_sided) + { + continue; + } + + for (U32 variant = 0; variant < LLGLSLShader::NUM_GLTF_VARIANTS; ++variant) + { + U32 attribute_mask = 0; + // for each mesh + for (auto& mesh : mMeshes) + { + // for each primitive + for (auto& primitive : mesh.mPrimitives) + { + if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant) + { + // accumulate vertex and index counts + primitive.mVertexOffset = vertex_count[variant]; + primitive.mIndexOffset = index_count[variant]; + + vertex_count[variant] += primitive.getVertexCount(); + index_count[variant] += primitive.getIndexCount(); + + // all primitives of a given variant and material should all have the same attribute mask + llassert(attribute_mask == 0 || primitive.mAttributeMask == attribute_mask); + attribute_mask |= primitive.mAttributeMask; + } + } + } + + // allocate vertex buffer and pack it + if (vertex_count[variant] > 0) + { + U32 mat_idx = mat_id + 1; + LLVertexBuffer* vb = new LLVertexBuffer(attribute_mask); + + rd.mBatches[variant][mat_idx].mVertexBuffer = vb; + vb->allocateBuffer(vertex_count[variant], + index_count[variant] * 2); // hack double index count... TODO: find a better way to indicate 32-bit indices will be used + vb->setBuffer(); + + for (auto& mesh : mMeshes) + { + for (auto& primitive : mesh.mPrimitives) + { + if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant) + { + primitive.upload(vb); + } + } + } + + vb->unmapBuffer(); + + vb->unbind(); + } + } + } + } + + // sanity check that all primitives have a vertex buffer + for (auto& mesh : mMeshes) + { + for (auto& primitive : mesh.mPrimitives) + { + llassert(primitive.mVertexBuffer.notNull()); + } + } + + // build render batches + for (S32 node_id = 0; node_id < mNodes.size(); ++node_id) + { + Node& node = mNodes[node_id]; + + if (node.mMesh != INVALID_INDEX) + { + auto& mesh = mMeshes[node.mMesh]; + + S32 mat_idx = mesh.mPrimitives[0].mMaterial + 1; + + S32 double_sided = mat_idx == 0 ? 0 : mMaterials[mat_idx - 1].mDoubleSided; + + for (S32 j = 0; j < mesh.mPrimitives.size(); ++j) + { + auto& primitive = mesh.mPrimitives[j]; + + S32 variant = primitive.mShaderVariant; + + RenderData& rd = mRenderData[double_sided]; + RenderBatch& rb = rd.mBatches[variant][mat_idx]; + + rb.mPrimitives.push_back({ j, node_id }); + } + } + } return true; } @@ -455,6 +673,7 @@ Asset::Asset(const Value& src) bool Asset::load(std::string_view filename) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; mFilename = filename; std::string ext = gDirUtilp->getExtension(mFilename); @@ -903,14 +1122,14 @@ bool Image::save(Asset& asset, const std::string& folder) return true; } -void Material::TextureInfo::serialize(object& dst) const +void TextureInfo::serialize(object& dst) const { write(mIndex, "index", dst, INVALID_INDEX); write(mTexCoord, "texCoord", dst, 0); write_extensions(dst, &mTextureTransform, "KHR_texture_transform"); } -S32 Material::TextureInfo::getTexCoord() const +S32 TextureInfo::getTexCoord() const { if (mTextureTransform.mPresent && mTextureTransform.mTexCoord != INVALID_INDEX) { @@ -928,7 +1147,7 @@ bool Material::isMultiUV() const mEmissiveTexture.getTexCoord() != 0; } -const Material::TextureInfo& Material::TextureInfo::operator=(const Value& src) +const TextureInfo& TextureInfo::operator=(const Value& src) { if (src.is_object()) { @@ -940,23 +1159,23 @@ const Material::TextureInfo& Material::TextureInfo::operator=(const Value& src) return *this; } -bool Material::TextureInfo::operator==(const Material::TextureInfo& rhs) const +bool TextureInfo::operator==(const TextureInfo& rhs) const { return mIndex == rhs.mIndex && mTexCoord == rhs.mTexCoord; } -bool Material::TextureInfo::operator!=(const Material::TextureInfo& rhs) const +bool TextureInfo::operator!=(const TextureInfo& rhs) const { return !(*this == rhs); } -void Material::OcclusionTextureInfo::serialize(object& dst) const +void OcclusionTextureInfo::serialize(object& dst) const { TextureInfo::serialize(dst); write(mStrength, "strength", dst, 1.f); } -const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=(const Value& src) +const OcclusionTextureInfo& OcclusionTextureInfo::operator=(const Value& src) { TextureInfo::operator=(src); @@ -968,13 +1187,13 @@ const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=( return *this; } -void Material::NormalTextureInfo::serialize(object& dst) const +void NormalTextureInfo::serialize(object& dst) const { TextureInfo::serialize(dst); write(mScale, "scale", dst, 1.f); } -const Material::NormalTextureInfo& Material::NormalTextureInfo::operator=(const Value& src) +const NormalTextureInfo& NormalTextureInfo::operator=(const Value& src) { TextureInfo::operator=(src); if (src.is_object()) @@ -1035,18 +1254,12 @@ void Material::Unlit::serialize(object& dst) const // no members and object has already been created, nothing to do } -void TextureTransform::getPacked(F32* packed) const +void TextureTransform::getPacked(vec4* packed) const { - packed[0] = mScale.x; - packed[1] = mScale.y; - packed[2] = mRotation; - packed[3] = mOffset.x; - packed[4] = mOffset.y; - - packed[5] = packed[6] = packed[7] = 0.f; + packed[0] = vec4(mScale.x, mScale.y, mRotation, mOffset.x); + packed[1] = vec4(mOffset.y, 0.f, 0.f, 0.f); } - const TextureTransform& TextureTransform::operator=(const Value& src) { mPresent = true; diff --git a/indra/newview/gltf/asset.h b/indra/newview/gltf/asset.h index ea3f7d480a..27821659db 100644 --- a/indra/newview/gltf/asset.h +++ b/indra/newview/gltf/asset.h @@ -34,6 +34,7 @@ #include "boost/json.hpp" #include "common.h" #include "../llviewertexture.h" +#include "llglslshader.h" extern F32SecondsImplicit gFrameTimeSeconds; @@ -65,14 +66,51 @@ namespace LL vec2 mScale = vec2(1.f, 1.f); S32 mTexCoord = INVALID_INDEX; - // get the texture transform as a packed array of floats - // dst MUST point to at least 8 floats - void getPacked(F32* dst) const; + // get the texture transform as a packed array of vec4's + // dst MUST point to at least 2 vec4's + void getPacked(vec4* dst) const; const TextureTransform& operator=(const Value& src); void serialize(boost::json::object& dst) const; }; + class TextureInfo + { + public: + S32 mIndex = INVALID_INDEX; + S32 mTexCoord = 0; + + TextureTransform mTextureTransform; + + bool operator==(const TextureInfo& rhs) const; + bool operator!=(const TextureInfo& rhs) const; + + // get the UV channel that should be used for sampling this texture + // returns mTextureTransform.mTexCoord if present and valid, otherwise mTexCoord + S32 getTexCoord() const; + + const TextureInfo& operator=(const Value& src); + void serialize(boost::json::object& dst) const; + }; + + class NormalTextureInfo : public TextureInfo + { + public: + F32 mScale = 1.0f; + + const NormalTextureInfo& operator=(const Value& src); + void serialize(boost::json::object& dst) const; + }; + + class OcclusionTextureInfo : public TextureInfo + { + public: + F32 mStrength = 1.0f; + + const OcclusionTextureInfo& operator=(const Value& src); + void serialize(boost::json::object& dst) const; + }; + class Material { public: @@ -91,42 +129,6 @@ namespace LL BLEND }; - class TextureInfo - { - public: - S32 mIndex = INVALID_INDEX; - S32 mTexCoord = 0; - - TextureTransform mTextureTransform; - - bool operator==(const TextureInfo& rhs) const; - bool operator!=(const TextureInfo& rhs) const; - - // get the UV channel that should be used for sampling this texture - // returns mTextureTransform.mTexCoord if present and valid, otherwise mTexCoord - S32 getTexCoord() const; - - const TextureInfo& operator=(const Value& src); - void serialize(boost::json::object& dst) const; - }; - - class NormalTextureInfo : public TextureInfo - { - public: - F32 mScale = 1.0f; - - const NormalTextureInfo& operator=(const Value& src); - void serialize(boost::json::object& dst) const; - }; - - class OcclusionTextureInfo : public TextureInfo - { - public: - F32 mStrength = 1.0f; - - const OcclusionTextureInfo& operator=(const Value& src); - void serialize(boost::json::object& dst) const; - }; class PbrMetallicRoughness { @@ -179,7 +181,6 @@ namespace LL { public: mat4 mMatrix = glm::identity<mat4>(); //local transform - mat4 mRenderMatrix; //transform for rendering mat4 mAssetMatrix; //transform from local to asset space mat4 mAssetMatrixInv; //transform from asset to local space @@ -206,10 +207,6 @@ namespace LL const Node& operator=(const Value& src); void serialize(boost::json::object& dst) const; - // Set mRenderMatrix to a transform that can be used for the current render pass - // modelview -- parent's render matrix - void updateRenderTransforms(Asset& asset, const mat4& modelview); - // update mAssetMatrix and mAssetMatrixInv void updateTransforms(Asset& asset, const mat4& parentMatrix); @@ -322,6 +319,31 @@ namespace LL bool prep(Asset& asset); }; + // Render Batch -- vertex buffer and list of primitives to render using + // said vertex buffer + class RenderBatch + { + public: + struct PrimitiveData + { + S32 mPrimitiveIndex = INVALID_INDEX; + S32 mNodeIndex = INVALID_INDEX; + }; + + LLPointer<LLVertexBuffer> mVertexBuffer; + std::vector<PrimitiveData> mPrimitives; + }; + + class RenderData + { + public: + // list of render batches + // indexed by [material index + 1](0 is reserved for default material) + // there should be exactly one render batch per material per variant + std::vector<RenderBatch> mBatches[LLGLSLShader::NUM_GLTF_VARIANTS]; + }; + + // C++ representation of a GLTF Asset class Asset { @@ -359,6 +381,16 @@ namespace LL // the last time update() was called according to gFrameTimeSeconds F32 mLastUpdateTime = gFrameTimeSeconds; + // data used for rendering + // 0 - single sided + // 1 - double sided + RenderData mRenderData[2]; + + // UBO for storing node transforms + U32 mNodesUBO = 0; + + // UBO for storing material data + U32 mMaterialsUBO = 0; // prepare for first time use bool prep(); @@ -373,8 +405,11 @@ namespace LL // update asset-to-node and node-to-asset transforms void updateTransforms(); - // update node render transforms - void updateRenderTransforms(const mat4& modelview); + // upload matrices to UBO + void uploadTransforms(); + + // upload materils to UBO + void uploadMaterials(); // return the index of the node that the line segment intersects with, or -1 if no hit // input and output values must be in this asset's local coordinate frame diff --git a/indra/newview/gltf/common.h b/indra/newview/gltf/common.h index 4f660d7cfc..b9698d4017 100644 --- a/indra/newview/gltf/common.h +++ b/indra/newview/gltf/common.h @@ -64,6 +64,9 @@ namespace LL class Asset; class Material; + class TextureInfo; + class NormalTextureInfo; + class OcclusionTextureInfo; class Mesh; class Node; class Scene; @@ -78,6 +81,17 @@ namespace LL class Accessor; class BufferView; class Buffer; + + enum class TextureType : U8 + { + BASE_COLOR = 0, + NORMAL, + METALLIC_ROUGHNESS, + OCCLUSION, + EMISSIVE + }; + + constexpr U32 TEXTURE_TYPE_COUNT = 5; } } diff --git a/indra/newview/gltf/primitive.cpp b/indra/newview/gltf/primitive.cpp index 2280c7004e..e1579374d4 100644 --- a/indra/newview/gltf/primitive.cpp +++ b/indra/newview/gltf/primitive.cpp @@ -380,11 +380,22 @@ bool Primitive::prep(Asset& asset) } } } + else + { //everything must be indexed at runtime + mIndexArray.resize(mPositions.size()); + for (U32 i = 0; i < mPositions.size(); ++i) + { + mIndexArray[i] = i; + } + } U32 mask = LLVertexBuffer::MAP_VERTEX; + mShaderVariant = 0; + if (!mWeights.empty()) { + mShaderVariant |= LLGLSLShader::GLTFVariant::RIGGED; mask |= LLVertexBuffer::MAP_WEIGHT4; mask |= LLVertexBuffer::MAP_JOINT; } @@ -406,9 +417,6 @@ bool Primitive::prep(Asset& asset) mColors.resize(mPositions.size(), LLColor4U::white); } - mShaderVariant = 0; - - // TODO: support colorless vertex buffers mask |= LLVertexBuffer::MAP_COLOR; bool unlit = false; @@ -506,69 +514,79 @@ bool Primitive::prep(Asset& asset) mask |= LLVertexBuffer::MAP_TANGENT; } - if (LLGLSLShader::sCurBoundShaderPtr == nullptr) - { // make sure a shader is bound to satisfy mVertexBuffer->setBuffer - gDebugProgram.bind(); + mAttributeMask = mask; + + if (mMaterial != INVALID_INDEX) + { + Material& material = asset.mMaterials[mMaterial]; + if (material.mAlphaMode == Material::AlphaMode::BLEND) + { + mShaderVariant |= LLGLSLShader::GLTFVariant::ALPHA_BLEND; + } } - mVertexBuffer = new LLVertexBuffer(mask); + createOctree(); + + return true; +} + +void Primitive::upload(LLVertexBuffer* buffer) +{ + mVertexBuffer = buffer; // we store these buffer sizes as S32 elsewhere llassert(mPositions.size() <= size_t(S32_MAX)); llassert(mIndexArray.size() <= size_t(S32_MAX / 2)); - mVertexBuffer->allocateBuffer(U32(mPositions.size()), U32(mIndexArray.size() * 2)); // double the size of the index buffer for 32-bit indices - mVertexBuffer->setBuffer(); - mVertexBuffer->setPositionData(mPositions.data()); - mVertexBuffer->setColorData(mColors.data()); + llassert(mVertexBuffer != nullptr); + + // assert that buffer can hold this primitive + llassert(mVertexBuffer->getNumVerts() >= mPositions.size() + mVertexOffset); + llassert(mVertexBuffer->getNumIndices() >= mIndexArray.size() + mIndexOffset); + llassert(mVertexBuffer->getTypeMask() == mAttributeMask); + + U32 offset = mVertexOffset; + U32 count = getVertexCount(); + + mVertexBuffer->setPositionData(mPositions.data(), offset, count); + mVertexBuffer->setColorData(mColors.data(), offset, count); if (!mNormals.empty()) { - mVertexBuffer->setNormalData(mNormals.data()); + mVertexBuffer->setNormalData(mNormals.data(), offset, count); } if (!mTangents.empty()) { - mVertexBuffer->setTangentData(mTangents.data()); + mVertexBuffer->setTangentData(mTangents.data(), offset, count); } if (!mWeights.empty()) { - mShaderVariant |= LLGLSLShader::GLTFVariant::RIGGED; - mVertexBuffer->setWeight4Data(mWeights.data()); - mVertexBuffer->setJointData(mJoints.data()); + mVertexBuffer->setWeight4Data(mWeights.data(), offset, count); + mVertexBuffer->setJointData(mJoints.data(), offset, count); } // flip texcoord y, upload, then flip back (keep the off-spec data in vram only) vertical_flip(mTexCoords0); - mVertexBuffer->setTexCoord0Data(mTexCoords0.data()); + mVertexBuffer->setTexCoord0Data(mTexCoords0.data(), offset, count); vertical_flip(mTexCoords0); if (!mTexCoords1.empty()) { vertical_flip(mTexCoords1); - mVertexBuffer->setTexCoord1Data(mTexCoords1.data()); + mVertexBuffer->setTexCoord1Data(mTexCoords1.data(), offset, count); vertical_flip(mTexCoords1); } - if (!mIndexArray.empty()) { - mVertexBuffer->setIndexData(mIndexArray.data()); - } - - createOctree(); - - mVertexBuffer->unbind(); - - if (mMaterial != INVALID_INDEX) - { - Material& material = asset.mMaterials[mMaterial]; - if (material.mAlphaMode == Material::AlphaMode::BLEND) + std::vector<U32> index_array; + index_array.resize(mIndexArray.size()); + for (U32 i = 0; i < mIndexArray.size(); ++i) { - mShaderVariant |= LLGLSLShader::GLTFVariant::ALPHA_BLEND; + index_array[i] = mIndexArray[i] + mVertexOffset; } + mVertexBuffer->setIndexData(index_array.data(), mIndexOffset, getIndexCount()); } - - return true; } void initOctreeTriangle(LLVolumeTriangle* tri, F32 scaler, S32 i0, S32 i1, S32 i2, const LLVector4a& v0, const LLVector4a& v1, const LLVector4a& v2) @@ -616,7 +634,7 @@ void Primitive::createOctree() if (mMode == Mode::TRIANGLES) { - const U32 num_triangles = mVertexBuffer->getNumIndices() / 3; + const U32 num_triangles = getIndexCount() / 3; // Initialize all the triangles we need mOctreeTriangles.resize(num_triangles); @@ -640,7 +658,7 @@ void Primitive::createOctree() } else if (mMode == Mode::TRIANGLE_STRIP) { - const U32 num_triangles = mVertexBuffer->getNumIndices() - 2; + const U32 num_triangles = getIndexCount() - 2; // Initialize all the triangles we need mOctreeTriangles.resize(num_triangles); @@ -664,7 +682,7 @@ void Primitive::createOctree() } else if (mMode == Mode::TRIANGLE_FAN) { - const U32 num_triangles = mVertexBuffer->getNumIndices() - 2; + const U32 num_triangles = getIndexCount() - 2; // Initialize all the triangles we need mOctreeTriangles.resize(num_triangles); diff --git a/indra/newview/gltf/primitive.h b/indra/newview/gltf/primitive.h index 7cc05cf831..304eb26432 100644 --- a/indra/newview/gltf/primitive.h +++ b/indra/newview/gltf/primitive.h @@ -54,10 +54,7 @@ namespace LL ~Primitive(); - // GPU copy of mesh data - LLPointer<LLVertexBuffer> mVertexBuffer; - - // CPU copy of mesh data, keep these as LLVector types for compatibility with raycasting code + // CPU copy of mesh data std::vector<LLVector2> mTexCoords0; std::vector<LLVector2> mTexCoords1; std::vector<LLVector4a> mNormals; @@ -80,6 +77,17 @@ namespace LL // shader variant according to LLGLSLShader::GLTFVariant flags U8 mShaderVariant = 0; + // vertex attribute mask + U32 mAttributeMask = 0; + + // backpointer to vertex buffer (owned by Asset) + LLPointer<LLVertexBuffer> mVertexBuffer; + U32 mVertexOffset = 0; + U32 mIndexOffset = 0; + + U32 getVertexCount() const { return (U32) mPositions.size(); } + U32 getIndexCount() const { return (U32) mIndexArray.size(); } + std::unordered_map<std::string, S32> mAttributes; // create octree based on vertex buffer @@ -100,6 +108,11 @@ namespace LL const Primitive& operator=(const Value& src); bool prep(Asset& asset); + + // upload geometry to given vertex buffer + // asserts that buffer is bound + // asserts that buffer is valid for this primitive + void upload(LLVertexBuffer* buffer); }; } } |