From 1f57f3b152f559f52c35a95bc37e4895a4bd5a15 Mon Sep 17 00:00:00 2001 From: Zhou Zhenglong Date: Tue, 19 Sep 2023 19:05:46 +0800 Subject: [PATCH] multi light added --- editor/assets/primitives.fbx.meta | 73 +++----- .../pipeline/custom/NativeBuiltinUtils.cpp | 138 +++++++++++++++ .../pipeline/custom/NativeBuiltinUtils.h | 7 + .../pipeline/custom/NativeExecutor.cpp | 31 ++-- .../pipeline/custom/NativePipeline.cpp | 1 + .../pipeline/custom/NativePipelineFwd.h | 2 + .../pipeline/custom/NativePipelineTypes.cpp | 11 +- .../pipeline/custom/NativePipelineTypes.h | 62 ++++++- .../pipeline/custom/NativeRenderGraph.cpp | 8 +- .../pipeline/custom/NativeRenderQueue.cpp | 41 +++-- .../pipeline/custom/NativeSceneCulling.cpp | 160 +++++++++++++++++- .../renderer/pipeline/custom/NativeSetter.cpp | 1 + .../renderer/pipeline/custom/details/Map.h | 40 +++++ 13 files changed, 485 insertions(+), 90 deletions(-) diff --git a/editor/assets/primitives.fbx.meta b/editor/assets/primitives.fbx.meta index fdc6e41562b..cc3453b08e8 100644 --- a/editor/assets/primitives.fbx.meta +++ b/editor/assets/primitives.fbx.meta @@ -19,8 +19,8 @@ ], "subMetas": {}, "userData": { - "gltfIndex": 5, - "triangleCount": 1512 + "gltfIndex": 4, + "triangleCount": 2380 } }, "801ec": { @@ -59,12 +59,12 @@ "triangleCount": 200 } }, - "40ece": { + "38fd2": { "importer": "gltf-mesh", - "uuid": "1263d74c-8167-4928-91a6-4e2672411f47@40ece", + "uuid": "1263d74c-8167-4928-91a6-4e2672411f47@38fd2", "displayName": "", - "id": "40ece", - "name": "torus.mesh", + "id": "38fd2", + "name": "cone.mesh", "ver": "1.1.1", "imported": true, "files": [ @@ -74,15 +74,15 @@ "subMetas": {}, "userData": { "gltfIndex": 2, - "triangleCount": 2048 + "triangleCount": 72 } }, - "fc873": { + "40ece": { "importer": "gltf-mesh", - "uuid": "1263d74c-8167-4928-91a6-4e2672411f47@fc873", + "uuid": "1263d74c-8167-4928-91a6-4e2672411f47@40ece", "displayName": "", - "id": "fc873", - "name": "quad.mesh", + "id": "40ece", + "name": "torus.mesh", "ver": "1.1.1", "imported": true, "files": [ @@ -92,15 +92,15 @@ "subMetas": {}, "userData": { "gltfIndex": 3, - "triangleCount": 2 + "triangleCount": 2048 } }, - "a804a": { + "fc873": { "importer": "gltf-mesh", - "uuid": "1263d74c-8167-4928-91a6-4e2672411f47@a804a", + "uuid": "1263d74c-8167-4928-91a6-4e2672411f47@fc873", "displayName": "", - "id": "a804a", - "name": "box.mesh", + "id": "fc873", + "name": "quad.mesh", "ver": "1.1.1", "imported": true, "files": [ @@ -109,8 +109,8 @@ ], "subMetas": {}, "userData": { - "gltfIndex": 4, - "triangleCount": 12 + "gltfIndex": 5, + "triangleCount": 2 } }, "8abdc": { @@ -128,15 +128,15 @@ "subMetas": {}, "userData": { "gltfIndex": 6, - "triangleCount": 112 + "triangleCount": 128 } }, - "38fd2": { + "a804a": { "importer": "gltf-mesh", - "uuid": "1263d74c-8167-4928-91a6-4e2672411f47@38fd2", + "uuid": "1263d74c-8167-4928-91a6-4e2672411f47@a804a", "displayName": "", - "id": "38fd2", - "name": "cone.mesh", + "id": "a804a", + "name": "box.mesh", "ver": "1.1.1", "imported": true, "files": [ @@ -146,25 +146,9 @@ "subMetas": {}, "userData": { "gltfIndex": 7, - "triangleCount": 64 + "triangleCount": 12 } }, - "7a3ca": { - "importer": "gltf-material", - "uuid": "1263d74c-8167-4928-91a6-4e2672411f47@7a3ca", - "displayName": "", - "id": "7a3ca", - "name": "lambert2.material", - "userData": { - "gltfIndex": 0 - }, - "ver": "1.0.14", - "imported": true, - "files": [ - ".json" - ], - "subMetas": {} - }, "8d883": { "importer": "gltf-material", "uuid": "1263d74c-8167-4928-91a6-4e2672411f47@8d883", @@ -172,7 +156,7 @@ "id": "8d883", "name": "lambert1.material", "userData": { - "gltfIndex": 1 + "gltfIndex": 0 }, "ver": "1.0.14", "imported": true, @@ -219,15 +203,14 @@ "meshes": [ "1263d74c-8167-4928-91a6-4e2672411f47@801ec", "1263d74c-8167-4928-91a6-4e2672411f47@2e76e", + "1263d74c-8167-4928-91a6-4e2672411f47@38fd2", "1263d74c-8167-4928-91a6-4e2672411f47@40ece", - "1263d74c-8167-4928-91a6-4e2672411f47@fc873", - "1263d74c-8167-4928-91a6-4e2672411f47@a804a", "1263d74c-8167-4928-91a6-4e2672411f47@17020", + "1263d74c-8167-4928-91a6-4e2672411f47@fc873", "1263d74c-8167-4928-91a6-4e2672411f47@8abdc", - "1263d74c-8167-4928-91a6-4e2672411f47@38fd2" + "1263d74c-8167-4928-91a6-4e2672411f47@a804a" ], "materials": [ - "1263d74c-8167-4928-91a6-4e2672411f47@7a3ca", "1263d74c-8167-4928-91a6-4e2672411f47@8d883" ], "scenes": [ diff --git a/native/cocos/renderer/pipeline/custom/NativeBuiltinUtils.cpp b/native/cocos/renderer/pipeline/custom/NativeBuiltinUtils.cpp index 2aebd316999..9f408052350 100644 --- a/native/cocos/renderer/pipeline/custom/NativeBuiltinUtils.cpp +++ b/native/cocos/renderer/pipeline/custom/NativeBuiltinUtils.cpp @@ -26,6 +26,7 @@ #include "cocos/application/ApplicationManager.h" #include "cocos/renderer/gfx-base/GFXDef-common.h" #include "cocos/renderer/gfx-base/GFXDevice.h" +#include "cocos/renderer/pipeline/Define.h" #include "cocos/renderer/pipeline/PipelineSceneData.h" #include "cocos/renderer/pipeline/custom/LayoutGraphTypes.h" #include "cocos/renderer/pipeline/custom/NativePipelineTypes.h" @@ -537,6 +538,143 @@ void setLegacyTextureUBOView( // setTextureImpl(data, layoutGraph, "cc_spotShadowMap", BuiltinResMgr::getInstance()->get("default-texture")->getGFXTexture()); } +namespace { + +float kLightMeterScale{10000.0F}; + +} // namespace + +void setLightUBO( + const scene::Light *light, bool bHDR, float exposure, + const scene::Shadows *shadowInfo, + char *buffer, size_t bufferSize) { + CC_EXPECTS(bufferSize % sizeof(float) == 0); + const auto maxSize = bufferSize / sizeof(float); + auto *lightBufferData = reinterpret_cast(buffer); + + size_t offset = 0; + + Vec3 position = Vec3(0.0F, 0.0F, 0.0F); + float size = 0.F; + float range = 0.F; + float luminanceHDR = 0.F; + float luminanceLDR = 0.F; + if (light->getType() == scene::LightType::SPHERE) { + const auto *sphereLight = static_cast(light); + position = sphereLight->getPosition(); + size = sphereLight->getSize(); + range = sphereLight->getRange(); + luminanceHDR = sphereLight->getLuminanceHDR(); + luminanceLDR = sphereLight->getLuminanceLDR(); + } else if (light->getType() == scene::LightType::SPOT) { + const auto *spotLight = static_cast(light); + position = spotLight->getPosition(); + size = spotLight->getSize(); + range = spotLight->getRange(); + luminanceHDR = spotLight->getLuminanceHDR(); + luminanceLDR = spotLight->getLuminanceLDR(); + } else if (light->getType() == scene::LightType::POINT) { + const auto *pointLight = static_cast(light); + position = pointLight->getPosition(); + size = 0.0F; + range = pointLight->getRange(); + luminanceHDR = pointLight->getLuminanceHDR(); + luminanceLDR = pointLight->getLuminanceLDR(); + } else if (light->getType() == scene::LightType::RANGED_DIRECTIONAL) { + const auto *rangedDirLight = static_cast(light); + position = rangedDirLight->getPosition(); + size = 0.0F; + range = 0.0F; + luminanceHDR = rangedDirLight->getIlluminanceHDR(); + luminanceLDR = rangedDirLight->getIlluminanceLDR(); + } + auto index = offset + pipeline::UBOForwardLight::LIGHT_POS_OFFSET; + CC_EXPECTS(index + 4 < maxSize); + lightBufferData[index++] = position.x; + lightBufferData[index++] = position.y; + lightBufferData[index] = position.z; + + index = offset + pipeline::UBOForwardLight::LIGHT_SIZE_RANGE_ANGLE_OFFSET; + CC_EXPECTS(index + 4 < maxSize); + lightBufferData[index++] = size; + lightBufferData[index] = range; + + index = offset + pipeline::UBOForwardLight::LIGHT_COLOR_OFFSET; + CC_EXPECTS(index + 4 < maxSize); + const auto &color = light->getColor(); + if (light->isUseColorTemperature()) { + const auto &tempRGB = light->getColorTemperatureRGB(); + lightBufferData[index++] = color.x * tempRGB.x; + lightBufferData[index++] = color.y * tempRGB.y; + lightBufferData[index++] = color.z * tempRGB.z; + } else { + lightBufferData[index++] = color.x; + lightBufferData[index++] = color.y; + lightBufferData[index++] = color.z; + } + + if (bHDR) { + lightBufferData[index] = luminanceHDR * exposure * kLightMeterScale; + } else { + lightBufferData[index] = luminanceLDR; + } + + switch (light->getType()) { + case scene::LightType::SPHERE: + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_POS_OFFSET + 3] = + static_cast(scene::LightType::SPHERE); + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_SIZE_RANGE_ANGLE_OFFSET + 2] = 0; + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_SIZE_RANGE_ANGLE_OFFSET + 3] = 0; + break; + case scene::LightType::SPOT: { + const auto *spotLight = static_cast(light); + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_POS_OFFSET + 3] = static_cast(scene::LightType::SPOT); + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_SIZE_RANGE_ANGLE_OFFSET + 2] = spotLight->getSpotAngle(); + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_SIZE_RANGE_ANGLE_OFFSET + 3] = + (shadowInfo->isEnabled() && + spotLight->isShadowEnabled() && + shadowInfo->getType() == scene::ShadowType::SHADOW_MAP) + ? 1.0F + : 0.0F; + + index = offset + pipeline::UBOForwardLight::LIGHT_DIR_OFFSET; + const auto &direction = spotLight->getDirection(); + lightBufferData[index++] = direction.x; + lightBufferData[index++] = direction.y; + lightBufferData[index] = direction.z; + } break; + case scene::LightType::POINT: + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_POS_OFFSET + 3] = static_cast(scene::LightType::POINT); + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_SIZE_RANGE_ANGLE_OFFSET + 2] = 0; + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_SIZE_RANGE_ANGLE_OFFSET + 3] = 0; + break; + case scene::LightType::RANGED_DIRECTIONAL: { + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_POS_OFFSET + 3] = static_cast(scene::LightType::RANGED_DIRECTIONAL); + + const auto *rangedDirLight = static_cast(light); + const Vec3 &right = rangedDirLight->getRight(); + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_SIZE_RANGE_ANGLE_OFFSET + 0] = right.x; + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_SIZE_RANGE_ANGLE_OFFSET + 1] = right.y; + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_SIZE_RANGE_ANGLE_OFFSET + 2] = right.z; + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_SIZE_RANGE_ANGLE_OFFSET + 3] = 0; + + const auto &direction = rangedDirLight->getDirection(); + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_DIR_OFFSET + 0] = direction.x; + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_DIR_OFFSET + 1] = direction.y; + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_DIR_OFFSET + 2] = direction.z; + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_DIR_OFFSET + 3] = 0; + + const auto &scale = rangedDirLight->getScale(); + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_BOUNDING_SIZE_VS_OFFSET + 0] = scale.x * 0.5F; + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_BOUNDING_SIZE_VS_OFFSET + 1] = scale.y * 0.5F; + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_BOUNDING_SIZE_VS_OFFSET + 2] = scale.z * 0.5F; + lightBufferData[offset + pipeline::UBOForwardLight::LIGHT_BOUNDING_SIZE_VS_OFFSET + 3] = 0; + } break; + default: + break; + } +} + const BuiltinCascadedShadowMap *getBuiltinShadowCSM( const PipelineRuntime &pplRuntime, const scene::Camera &camera, diff --git a/native/cocos/renderer/pipeline/custom/NativeBuiltinUtils.h b/native/cocos/renderer/pipeline/custom/NativeBuiltinUtils.h index 1b1d8ebb6a2..9d5ad1814e1 100644 --- a/native/cocos/renderer/pipeline/custom/NativeBuiltinUtils.h +++ b/native/cocos/renderer/pipeline/custom/NativeBuiltinUtils.h @@ -37,6 +37,7 @@ namespace scene { class Camera; class Light; class DirectionalLight; +class Shadows; } // namespace scene namespace geometry { @@ -84,6 +85,12 @@ void setShadowUBOLightView( uint32_t level, RenderData &data); +// Additive light +void setLightUBO( + const scene::Light *light, bool bHDR, float exposure, + const scene::Shadows *shadowInfo, + char *buffer, size_t bufferSize); + // Render graph void updateRasterPassConstants(uint32_t width, uint32_t height, Setter &setter); diff --git a/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp b/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp index 451942a2ff7..78c45082cb6 100644 --- a/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp +++ b/native/cocos/renderer/pipeline/custom/NativeExecutor.cpp @@ -28,6 +28,7 @@ #include "LayoutGraphGraphs.h" #include "LayoutGraphTypes.h" #include "LayoutGraphUtils.h" +#include "NativeBuiltinUtils.h" #include "NativePipelineFwd.h" #include "NativePipelineTypes.h" #include "NativeRenderGraphUtils.h" @@ -991,9 +992,6 @@ struct RenderGraphUploadVisitor : boost::dfs_visitor<> { updateAndCreatePerPassDescriptorSet(vertID); ctx.currentProjMatrix = sceneData.camera->getMatProj(); } - if (sceneData.shadingLight && !dynamic_cast(sceneData.shadingLight.get())) { - - } } else if (holds(vertID, ctx.g)) { const auto& blit = get(BlitTag{}, vertID, ctx.g); if (blit.camera) { @@ -1385,14 +1383,9 @@ struct RenderGraphVisitor : boost::dfs_visitor<> { const auto* scene = camera->getScene(); const auto& queueDesc = ctx.context.sceneCulling.renderQueueIndex.at(sceneID); const auto& queue = ctx.context.sceneCulling.renderQueues[queueDesc.renderQueueTarget.value]; - queue.opaqueQueue.recordCommandBuffer( - ctx.device, camera, ctx.currentPass, ctx.cmdBuff, 0); - queue.opaqueInstancingQueue.recordCommandBuffer( - ctx.currentPass, ctx.cmdBuff); - queue.transparentQueue.recordCommandBuffer( - ctx.device, camera, ctx.currentPass, ctx.cmdBuff, 0); - queue.transparentInstancingQueue.recordCommandBuffer( - ctx.currentPass, ctx.cmdBuff); + + queue.recordCommands(ctx.cmdBuff, ctx.currentPass, 0); + if (any(sceneData.flags & SceneFlags::REFLECTION_PROBE)) { queue.probeQueue.removeMacro(); } @@ -1998,6 +1991,16 @@ void NativePipeline::executeRenderGraph(const RenderGraph& rg) { } } + // light manangement + { + auto& ctx = ppl.nativeContext; + ctx.lightResources.clear(); + ctx.lightResources.buildLights( + ctx.sceneCulling, + ppl.pipelineSceneData->isHDR(), + ppl.pipelineSceneData->getShadows()); + } + // gpu driven if constexpr (ENABLE_GPU_DRIVEN) { // TODO(jilin): consider populating renderSceneResources here @@ -2019,6 +2022,7 @@ void NativePipeline::executeRenderGraph(const RenderGraph& rg) { // upload buffers { + // scene const auto& sceneCulling = ppl.nativeContext.sceneCulling; for (uint32_t queueID = 0; queueID != sceneCulling.numRenderQueues; ++queueID) { // notice: we cannot use ranged-for of sceneCulling.renderQueues @@ -2027,6 +2031,11 @@ void NativePipeline::executeRenderGraph(const RenderGraph& rg) { queue.opaqueInstancingQueue.uploadBuffers(submit.primaryCommandBuffer); queue.transparentInstancingQueue.uploadBuffers(submit.primaryCommandBuffer); } + + // lights + auto& ctx = ppl.nativeContext; + ctx.lightResources.buildLightBuffer(submit.primaryCommandBuffer); + ctx.lightResources.tryUpdateRenderSceneLocalDescriptorSet(sceneCulling); } ccstd::pmr::unordered_map< diff --git a/native/cocos/renderer/pipeline/custom/NativePipeline.cpp b/native/cocos/renderer/pipeline/custom/NativePipeline.cpp index da2643078ad..85cec43b9cf 100644 --- a/native/cocos/renderer/pipeline/custom/NativePipeline.cpp +++ b/native/cocos/renderer/pipeline/custom/NativePipeline.cpp @@ -1195,6 +1195,7 @@ bool NativePipeline::activate(gfx::Swapchain *swapchainIn) { const auto &lg = programLibrary->layoutGraph; const auto numNodes = num_vertices(lg); nativeContext.layoutGraphResources.reserve(numNodes); + nativeContext.lightResources.init(*programLibrary, device, 16); for (uint32_t i = 0; i != numNodes; ++i) { auto &node = nativeContext.layoutGraphResources.emplace_back(); diff --git a/native/cocos/renderer/pipeline/custom/NativePipelineFwd.h b/native/cocos/renderer/pipeline/custom/NativePipelineFwd.h index 29fc3204c2d..f83927c1b64 100644 --- a/native/cocos/renderer/pipeline/custom/NativePipelineFwd.h +++ b/native/cocos/renderer/pipeline/custom/NativePipelineFwd.h @@ -91,7 +91,9 @@ struct LightBoundsCullingKey; struct LightBoundsCulling; struct NativeRenderQueueID; struct NativeRenderQueueDesc; +struct LightBoundsCullingResult; struct SceneCulling; +struct LightResource; struct NativeRenderContext; class NativeProgramLibrary; struct PipelineCustomization; diff --git a/native/cocos/renderer/pipeline/custom/NativePipelineTypes.cpp b/native/cocos/renderer/pipeline/custom/NativePipelineTypes.cpp index 1d4f2cdc285..4046c22bc68 100644 --- a/native/cocos/renderer/pipeline/custom/NativePipelineTypes.cpp +++ b/native/cocos/renderer/pipeline/custom/NativePipelineTypes.cpp @@ -90,7 +90,8 @@ NativeRenderQueue::NativeRenderQueue(NativeRenderQueue&& rhs, const allocator_ty opaqueInstancingQueue(std::move(rhs.opaqueInstancingQueue), alloc), transparentInstancingQueue(std::move(rhs.transparentInstancingQueue), alloc), sceneFlags(rhs.sceneFlags), - subpassOrPassLayoutID(rhs.subpassOrPassLayoutID) {} + subpassOrPassLayoutID(rhs.subpassOrPassLayoutID), + lightByteOffset(rhs.lightByteOffset) {} ResourceGroup::ResourceGroup(const allocator_type& alloc) noexcept : instancingBuffers(alloc) {} @@ -209,12 +210,18 @@ SceneCulling::SceneCulling(SceneCulling&& rhs, const allocator_type& alloc) numRenderQueues(rhs.numRenderQueues), gpuCullingPassID(rhs.gpuCullingPassID) {} +LightResource::LightResource(const allocator_type& alloc) noexcept +: cpuBuffer(alloc), + lights(alloc), + lightIndex(alloc) {} + NativeRenderContext::NativeRenderContext(std::unique_ptr defaultResourceIn, const allocator_type& alloc) noexcept : defaultResource(std::move(defaultResourceIn)), resourceGroups(alloc), layoutGraphResources(alloc), renderSceneResources(alloc), - sceneCulling(alloc) {} + sceneCulling(alloc), + lightResources(alloc) {} NativeProgramLibrary::NativeProgramLibrary(const allocator_type& alloc) noexcept : layoutGraph(alloc), diff --git a/native/cocos/renderer/pipeline/custom/NativePipelineTypes.h b/native/cocos/renderer/pipeline/custom/NativePipelineTypes.h index 06216cc2e83..c17bd4871b5 100644 --- a/native/cocos/renderer/pipeline/custom/NativePipelineTypes.h +++ b/native/cocos/renderer/pipeline/custom/NativePipelineTypes.h @@ -995,9 +995,9 @@ struct RenderInstancingQueue { void sort(); void uploadBuffers(gfx::CommandBuffer *cmdBuffer) const; void recordCommandBuffer( - gfx::RenderPass *renderPass, gfx::CommandBuffer *cmdBuffer, - gfx::DescriptorSet *ds = nullptr, uint32_t offset = 0, - const ccstd::vector *dynamicOffsets = nullptr) const; + gfx::RenderPass *renderPass, uint32_t subpassIndex, + gfx::CommandBuffer *cmdBuffer, + uint32_t lightByteOffset = 0xFFFFFFFF) const; ccstd::pmr::vector sortedBatches; PmrUnorderedMap passInstances; @@ -1061,9 +1061,10 @@ struct RenderDrawQueue { void add(const scene::Model& model, float depth, uint32_t subModelIdx, uint32_t passIdx); void sortOpaqueOrCutout(); void sortTransparent(); - void recordCommandBuffer(gfx::Device *device, const scene::Camera *camera, - gfx::RenderPass *renderPass, gfx::CommandBuffer *cmdBuffer, - uint32_t subpassIndex) const; + void recordCommandBuffer( + gfx::RenderPass *renderPass, uint32_t subpassIndex, + gfx::CommandBuffer *cmdBuffer, + uint32_t lightByteOffset = 0xFFFFFFFF) const; ccstd::pmr::vector instances; }; @@ -1086,6 +1087,8 @@ struct NativeRenderQueue { void sort(); void clear() noexcept; bool empty() const noexcept; + void recordCommands( + gfx::CommandBuffer *cmdBuffer, gfx::RenderPass *renderPass, uint32_t subpassIndex) const; RenderDrawQueue opaqueQueue; RenderDrawQueue transparentQueue; @@ -1094,6 +1097,7 @@ struct NativeRenderQueue { RenderInstancingQueue transparentInstancingQueue; SceneFlags sceneFlags{SceneFlags::NONE}; uint32_t subpassOrPassLayoutID{0xFFFFFFFF}; + uint32_t lightByteOffset{0xFFFFFFFF}; }; struct ResourceGroup { @@ -1219,7 +1223,7 @@ struct LayoutGraphNodeResource { LayoutGraphNodeResource& operator=(LayoutGraphNodeResource const& rhs) = delete; void syncResources() noexcept; - PmrFlatMap uniformBuffers; + ccstd::pmr::unordered_map uniformBuffers; DescriptorSetPool descriptorSetPool; PmrTransparentMap programResources; }; @@ -1369,6 +1373,11 @@ struct NativeRenderQueueDesc { scene::LightType lightType{scene::LightType::UNKNOWN}; }; +struct LightBoundsCullingResult { + ccstd::vector instances; + uint32_t lightByteOffset{0xFFFFFFFF}; +}; + struct SceneCulling { using allocator_type = boost::container::pmr::polymorphic_allocator; allocator_type get_allocator() const noexcept { // NOLINT @@ -1397,7 +1406,7 @@ struct SceneCulling { ccstd::pmr::unordered_map frustumCullings; ccstd::pmr::vector> frustumCullingResults; ccstd::pmr::unordered_map lightBoundsCullings; - ccstd::pmr::vector> lightBoundsCullingResults; + ccstd::pmr::vector lightBoundsCullingResults; ccstd::pmr::vector renderQueues; PmrFlatMap renderQueueIndex; uint32_t numFrustumCulling{0}; @@ -1406,6 +1415,42 @@ struct SceneCulling { uint32_t gpuCullingPassID{0xFFFFFFFF}; }; +struct LightResource { + using allocator_type = boost::container::pmr::polymorphic_allocator; + allocator_type get_allocator() const noexcept { // NOLINT + return {cpuBuffer.get_allocator().resource()}; + } + + LightResource(const allocator_type& alloc) noexcept; // NOLINT + LightResource(LightResource&& rhs) = delete; + LightResource(LightResource const& rhs) = delete; + LightResource& operator=(LightResource&& rhs) = delete; + LightResource& operator=(LightResource const& rhs) = delete; + + void init(const NativeProgramLibrary& programLib, gfx::Device* deviceIn, uint32_t maxNumLights); + void buildLights( + SceneCulling& sceneCulling, + bool bHDR, + const scene::Shadows* shadowInfo); + void tryUpdateRenderSceneLocalDescriptorSet(const SceneCulling& sceneCulling); + void clear(); + + uint32_t addLight(const scene::Light* light, bool bHDR, float exposure, const scene::Shadows *shadowInfo); + void buildLightBuffer(gfx::CommandBuffer* cmdBuffer) const; + + ccstd::pmr::vector cpuBuffer; + const NativeProgramLibrary* programLibrary{nullptr}; + gfx::Device* device{nullptr}; + uint32_t elementSize{0}; + uint32_t maxNumLights{16}; + uint32_t binding{0xFFFFFFFF}; + bool resized{false}; + IntrusivePtr lightBuffer; + IntrusivePtr firstLightBufferView; + ccstd::pmr::vector lights; + PmrFlatMap lightIndex; +}; + struct NativeRenderContext { using allocator_type = boost::container::pmr::polymorphic_allocator; allocator_type get_allocator() const noexcept { // NOLINT @@ -1427,6 +1472,7 @@ struct NativeRenderContext { ccstd::pmr::unordered_map renderSceneResources; QuadResource fullscreenQuad; SceneCulling sceneCulling; + LightResource lightResources; }; class NativeProgramLibrary final : public ProgramLibrary { diff --git a/native/cocos/renderer/pipeline/custom/NativeRenderGraph.cpp b/native/cocos/renderer/pipeline/custom/NativeRenderGraph.cpp index f299e21eca5..304f76fbf5d 100644 --- a/native/cocos/renderer/pipeline/custom/NativeRenderGraph.cpp +++ b/native/cocos/renderer/pipeline/custom/NativeRenderGraph.cpp @@ -818,13 +818,11 @@ SceneBuilder *NativeRenderQueueBuilder::addScene( // objects are projected to camera, set camera ubo builder->setBuiltinCameraConstants(camera); - if (const auto *pDirLight = camera->getScene()->getMainLight(); pDirLight) { + if (const auto *pDirLight = dynamic_cast(light); pDirLight) { // light is directional builder->setBuiltinDirectionalLightConstants(pDirLight, camera); - } else if (light) { - // light is non-directional - // builder->setBuiltinXxxLightConstants(light, camera); - CC_EXPECTS(false); + } else if (light) { // light is non-directional + // TODO(zhouzhenglong): refactor per-instance lighting } // set builtin legacy ubo diff --git a/native/cocos/renderer/pipeline/custom/NativeRenderQueue.cpp b/native/cocos/renderer/pipeline/custom/NativeRenderQueue.cpp index 41525f49df2..d4b4f4c9d69 100644 --- a/native/cocos/renderer/pipeline/custom/NativeRenderQueue.cpp +++ b/native/cocos/renderer/pipeline/custom/NativeRenderQueue.cpp @@ -26,6 +26,7 @@ #include #include "NativePipelineTypes.h" #include "cocos/renderer/pipeline/Define.h" +#include "cocos/renderer/pipeline/InstancedBuffer.h" #include "cocos/renderer/pipeline/PipelineStateManager.h" #include "cocos/renderer/pipeline/custom/LayoutGraphGraphs.h" #include "cocos/renderer/pipeline/custom/details/GslUtils.h" @@ -131,9 +132,9 @@ void RenderDrawQueue::sortTransparent() { } void RenderDrawQueue::recordCommandBuffer( - gfx::Device * /*device*/, const scene::Camera * /*camera*/, - gfx::RenderPass *renderPass, gfx::CommandBuffer *cmdBuff, - uint32_t subpassIndex) const { + gfx::RenderPass *renderPass, uint32_t subpassIndex, + gfx::CommandBuffer *cmdBuff, + uint32_t lightByteOffset) const { for (const auto &instance : instances) { const auto *subModel = instance.subModel; @@ -145,7 +146,11 @@ void RenderDrawQueue::recordCommandBuffer( cmdBuff->bindPipelineState(pso); cmdBuff->bindDescriptorSet(pipeline::materialSet, pass->getDescriptorSet()); - cmdBuff->bindDescriptorSet(pipeline::localSet, subModel->getDescriptorSet()); + if (lightByteOffset != 0xFFFFFFFF) { + cmdBuff->bindDescriptorSet(pipeline::localSet, subModel->getDescriptorSet(), 1, &lightByteOffset); + } else { + cmdBuff->bindDescriptorSet(pipeline::localSet, subModel->getDescriptorSet()); + } cmdBuff->bindInputAssembler(inputAssembler); cmdBuff->draw(inputAssembler); } @@ -207,8 +212,9 @@ void RenderInstancingQueue::uploadBuffers(gfx::CommandBuffer *cmdBuffer) const { } void RenderInstancingQueue::recordCommandBuffer( - gfx::RenderPass *renderPass, gfx::CommandBuffer *cmdBuffer, - gfx::DescriptorSet *ds, uint32_t offset, const ccstd::vector *dynamicOffsets) const { + gfx::RenderPass *renderPass, uint32_t subpassIndex, + gfx::CommandBuffer *cmdBuffer, + uint32_t lightByteOffset) const { // const auto &renderQueue = sortedBatches; for (const auto *instanceBuffer : renderQueue) { if (!instanceBuffer->hasPendingModels()) { @@ -223,16 +229,13 @@ void RenderInstancingQueue::recordCommandBuffer( continue; } auto *pso = pipeline::PipelineStateManager::getOrCreatePipelineState( - drawPass, instance.shader, instance.ia, renderPass); + drawPass, instance.shader, instance.ia, renderPass, subpassIndex); if (lastPSO != pso) { cmdBuffer->bindPipelineState(pso); lastPSO = pso; } - if (ds) { - cmdBuffer->bindDescriptorSet(pipeline::globalSet, ds, 1, &offset); - } - if (dynamicOffsets) { - cmdBuffer->bindDescriptorSet(pipeline::localSet, instance.descriptorSet, *dynamicOffsets); + if (lightByteOffset != 0xFFFFFFFF) { + cmdBuffer->bindDescriptorSet(pipeline::localSet, instance.descriptorSet, 1, &lightByteOffset); } else { cmdBuffer->bindDescriptorSet(pipeline::localSet, instance.descriptorSet, instanceBuffer->dynamicOffsets()); } @@ -249,6 +252,20 @@ void NativeRenderQueue::sort() { transparentInstancingQueue.sort(); } +void NativeRenderQueue::recordCommands( + gfx::CommandBuffer *cmdBuffer, + gfx::RenderPass *renderPass, + uint32_t subpassIndex) const { + opaqueQueue.recordCommandBuffer( + renderPass, subpassIndex, cmdBuffer, lightByteOffset); + opaqueInstancingQueue.recordCommandBuffer( + renderPass, subpassIndex, cmdBuffer, lightByteOffset); + transparentQueue.recordCommandBuffer( + renderPass, subpassIndex, cmdBuffer, lightByteOffset); + transparentInstancingQueue.recordCommandBuffer( + renderPass, subpassIndex, cmdBuffer, lightByteOffset); +} + } // namespace render } // namespace cc diff --git a/native/cocos/renderer/pipeline/custom/NativeSceneCulling.cpp b/native/cocos/renderer/pipeline/custom/NativeSceneCulling.cpp index a9faffcf002..cc805461604 100644 --- a/native/cocos/renderer/pipeline/custom/NativeSceneCulling.cpp +++ b/native/cocos/renderer/pipeline/custom/NativeSceneCulling.cpp @@ -1,4 +1,5 @@ #include "cocos/renderer/pipeline/Define.h" +#include "cocos/renderer/pipeline/custom/LayoutGraphUtils.h" #include "cocos/renderer/pipeline/custom/NativeBuiltinUtils.h" #include "cocos/renderer/pipeline/custom/NativePipelineTypes.h" #include "cocos/renderer/pipeline/custom/NativeRenderGraphUtils.h" @@ -10,10 +11,14 @@ #include "cocos/scene/Skybox.h" #include "cocos/scene/SpotLight.h" +#include + namespace cc { namespace render { + const static uint32_t REFLECTION_PROBE_DEFAULT_MASK = ~static_cast(pipeline::LayerList::UI_2D) & ~static_cast(pipeline::LayerList::PROFILER) & ~static_cast(pipeline::LayerList::UI_3D) & ~static_cast(pipeline::LayerList::GIZMOS) & ~static_cast(pipeline::LayerList::SCENE_GIZMO) & ~static_cast(pipeline::LayerList::EDITOR); + void NativeRenderQueue::clear() noexcept { probeQueue.clear(); opaqueQueue.instances.clear(); @@ -433,27 +438,27 @@ void SceneCulling::batchLightBoundsCulling() { CC_EXPECTS(key.camera->getScene() == scene); const auto& frustumCullingResult = frustumCullingResults.at(key.frustumCullingID.value); auto& lightBoundsCullingResult = lightBoundsCullingResults.at(cullingID.value); - CC_EXPECTS(lightBoundsCullingResult.empty()); + CC_EXPECTS(lightBoundsCullingResult.instances.empty()); switch (key.cullingLight->getType()) { case scene::LightType::SPHERE: { const auto* light = dynamic_cast(key.cullingLight); CC_ENSURES(light); - executeSphereLightCulling(*light, frustumCullingResult, lightBoundsCullingResult); + executeSphereLightCulling(*light, frustumCullingResult, lightBoundsCullingResult.instances); } break; case scene::LightType::SPOT: { const auto* light = dynamic_cast(key.cullingLight); CC_ENSURES(light); - executeSpotLightCulling(*light, frustumCullingResult, lightBoundsCullingResult); + executeSpotLightCulling(*light, frustumCullingResult, lightBoundsCullingResult.instances); } break; case scene::LightType::POINT: { const auto* light = dynamic_cast(key.cullingLight); CC_ENSURES(light); - executePointLightCulling(*light, frustumCullingResult, lightBoundsCullingResult); + executePointLightCulling(*light, frustumCullingResult, lightBoundsCullingResult.instances); } break; case scene::LightType::RANGED_DIRECTIONAL: { const auto* light = dynamic_cast(key.cullingLight); CC_ENSURES(light); - executeRangedDirectionalLightCulling(*light, frustumCullingResult, lightBoundsCullingResult); + executeRangedDirectionalLightCulling(*light, frustumCullingResult, lightBoundsCullingResult.instances); } break; case scene::LightType::DIRECTIONAL: case scene::LightType::UNKNOWN: @@ -585,7 +590,7 @@ void SceneCulling::fillRenderQueues( // is culled by light bounds if (lightBoundsCullingID.value != 0xFFFFFFFF) { CC_EXPECTS(lightBoundsCullingID.value < lightBoundsCullingResults.size()); - return lightBoundsCullingResults.at(lightBoundsCullingID.value); + return lightBoundsCullingResults.at(lightBoundsCullingID.value).instances; } // not culled by light bounds return frustumCullingResults.at(frustomCulledResultID.value); @@ -633,7 +638,8 @@ void SceneCulling::clear() noexcept { // light bounds culling lightBoundsCullings.clear(); for (auto& c : lightBoundsCullingResults) { - c.clear(); + c.instances.clear(); + c.lightByteOffset = 0xFFFFFFFF; } // native render queues for (auto& q : renderQueues) { @@ -650,6 +656,146 @@ void SceneCulling::clear() noexcept { numRenderQueues = 0; } +void LightResource::init(const NativeProgramLibrary& programLib, gfx::Device* deviceIn, uint32_t maxNumLightsIn) { + CC_EXPECTS(!device); + device = deviceIn; + programLibrary = &programLib; + + const auto& instanceLayout = programLibrary->localLayoutData; + const auto attrID = at(programLib.layoutGraph.attributeIndex, std::string_view{"CCForwardLight"}); + const auto& uniformBlock = instanceLayout.uniformBlocks.at(attrID); + + elementSize = boost::alignment::align_up( + getUniformBlockSize(uniformBlock.members), + device->getCapabilities().uboOffsetAlignment); + maxNumLights = maxNumLightsIn; + binding = programLib.localLayoutData.bindingMap.at(attrID); + + const auto bufferSize = elementSize * maxNumLights; + + lightBuffer = device->createBuffer(gfx::BufferInfo{ + gfx::BufferUsageBit::UNIFORM | gfx::BufferUsageBit::TRANSFER_DST, + gfx::MemoryUsageBit::HOST | gfx::MemoryUsageBit::DEVICE, + bufferSize, + elementSize, + }); + firstLightBufferView = device->createBuffer({lightBuffer, 0, elementSize}); + + cpuBuffer.resize(bufferSize); + lights.reserve(maxNumLights); + lightIndex.reserve(maxNumLights); + + CC_ENSURES(elementSize); + CC_ENSURES(maxNumLights); + + resized = true; +} + +uint32_t LightResource::addLight( + const scene::Light* light, + bool bHDR, + float exposure, + const scene::Shadows* shadowInfo) { + // already added + auto iter = lightIndex.find(light); + if (iter != lightIndex.end()) { + return iter->second; + } + + // resize buffer + if (lights.size() == maxNumLights) { + resized = true; + maxNumLights *= 2; + const auto bufferSize = elementSize * maxNumLights; + lightBuffer->resize(bufferSize); + firstLightBufferView = device->createBuffer({lightBuffer, 0, elementSize}); + cpuBuffer.resize(bufferSize); + lights.reserve(maxNumLights); + lightIndex.reserve(maxNumLights); + } + CC_ENSURES(lights.size() < maxNumLights); + + // add light + const auto lightID = static_cast(lights.size()); + lights.emplace_back(light); + auto res = lightIndex.emplace(light, lightID); + CC_ENSURES(res.second); + + // update buffer + const auto offset = elementSize * lightID; + setLightUBO(light, bHDR, exposure, shadowInfo, cpuBuffer.data() + offset, elementSize); + + return lightID * elementSize; +} + +void LightResource::buildLights( + SceneCulling& sceneCulling, + bool bHDR, + const scene::Shadows* shadowInfo) { + // build light buffer + for (const auto& [scene, lightBoundsCullings] : sceneCulling.lightBoundsCullings) { + for (const auto& [key, lightBoundsCullingID] : lightBoundsCullings.resultIndex) { + float exposure = 1.0F; + if (key.camera) { + exposure = key.camera->getExposure(); + } else if (key.probe && key.probe->getCamera()) { + exposure = key.probe->getCamera()->getExposure(); + } else { + CC_EXPECTS(false); + } + const auto lightByteOffset = addLight( + key.cullingLight, + bHDR, + exposure, + shadowInfo); + + // save light byte offset for each light bounds culling + auto& result = sceneCulling.lightBoundsCullingResults.at(lightBoundsCullingID.value); + result.lightByteOffset = lightByteOffset; + } + } + + // assign light byte offset to each queue + for (const auto& [sceneID, desc] : sceneCulling.renderQueueIndex) { + if (desc.lightBoundsCulledResultID.value == 0xFFFFFFFF) { + continue; + } + const auto lightByteOffset = sceneCulling.lightBoundsCullingResults.at( + desc.lightBoundsCulledResultID.value) + .lightByteOffset; + + sceneCulling.renderQueues.at(desc.renderQueueTarget.value).lightByteOffset = lightByteOffset; + } +} + +void LightResource::clear() { + std::fill(cpuBuffer.begin(), cpuBuffer.end(), 0); + lights.clear(); + lightIndex.clear(); +} + +void LightResource::buildLightBuffer(gfx::CommandBuffer* cmdBuffer) const { + cmdBuffer->updateBuffer(lightBuffer, cpuBuffer.data(), lights.size() * elementSize); +} + +void LightResource::tryUpdateRenderSceneLocalDescriptorSet(const SceneCulling& sceneCulling) { + if (!resized) { + return; + } + + for (const auto& [scene, culling] : sceneCulling.frustumCullings) { + for (const auto& model : scene->getModels()) { + CC_EXPECTS(model); + for (const auto& submodel : model->getSubModels()) { + auto* set = submodel->getDescriptorSet(); + set->bindBuffer(binding, firstLightBufferView); + set->update(); + } + } + } + resized = false; +} + } // namespace render } // namespace cc diff --git a/native/cocos/renderer/pipeline/custom/NativeSetter.cpp b/native/cocos/renderer/pipeline/custom/NativeSetter.cpp index b06cfbbc52b..e78cf4fb073 100644 --- a/native/cocos/renderer/pipeline/custom/NativeSetter.cpp +++ b/native/cocos/renderer/pipeline/custom/NativeSetter.cpp @@ -168,6 +168,7 @@ void NativeSetter::setBuiltinSphereLightConstants(const scene::SphereLight *ligh } void NativeSetter::setBuiltinSpotLightConstants(const scene::SpotLight *light, const scene::Camera *camera) { + CC_EXPECTS(light); const auto &sceneData = *this->pipelineRuntime->getPipelineSceneData(); const auto &shadowInfo = *sceneData.getShadows(); diff --git a/native/cocos/renderer/pipeline/custom/details/Map.h b/native/cocos/renderer/pipeline/custom/details/Map.h index a82d85a0472..ae6be1cc54d 100644 --- a/native/cocos/renderer/pipeline/custom/details/Map.h +++ b/native/cocos/renderer/pipeline/custom/details/Map.h @@ -106,6 +106,46 @@ using PmrUnorderedStringMultiMap = std::unordered_multimap< TransparentStringHash, std::equal_to<>, boost::container::pmr::polymorphic_allocator>>; +template +inline typename std::map, Allocator>::mapped_type& +at(std::map, Allocator>& m, const KeyLike& key) { + auto iter = m.find(key); + if (iter == m.end()) { + throw std::out_of_range("at(std::map) out of range"); + } + return iter->second; +} + +template +inline typename std::map, Allocator>::mapped_type const& +at(const std::map, Allocator>& m, const KeyLike& key) { + auto iter = m.find(key); + if (iter == m.end()) { + throw std::out_of_range("at(std::map) out of range"); + } + return iter->second; +} + +template +inline typename boost::container::flat_map, Allocator>::mapped_type& +at(boost::container::flat_map, Allocator>& m, const KeyLike& key) { + auto iter = m.find(key); + if (iter == m.end()) { + throw std::out_of_range("at(boost::container::flat_map) out of range"); + } + return iter->second; +} + +template +inline typename boost::container::flat_map, Allocator>::mapped_type const& +at(const boost::container::flat_map, Allocator>& m, const KeyLike& key) { + auto iter = m.find(key); + if (iter == m.end()) { + throw std::out_of_range("at(boost::container::flat_map) out of range"); + } + return iter->second; +} + } // namespace cc namespace ccstd {