diff options
Diffstat (limited to 'Runtime/Camera/RenderLoops/ForwardShaderRenderLoop.cpp')
-rw-r--r-- | Runtime/Camera/RenderLoops/ForwardShaderRenderLoop.cpp | 1403 |
1 files changed, 1403 insertions, 0 deletions
diff --git a/Runtime/Camera/RenderLoops/ForwardShaderRenderLoop.cpp b/Runtime/Camera/RenderLoops/ForwardShaderRenderLoop.cpp new file mode 100644 index 0000000..44e91b8 --- /dev/null +++ b/Runtime/Camera/RenderLoops/ForwardShaderRenderLoop.cpp @@ -0,0 +1,1403 @@ +#include "UnityPrefix.h" +#include "Runtime/GfxDevice/GfxDeviceConfigure.h" + +#include "RenderLoopPrivate.h" +#include "RenderLoop.h" +#include "Runtime/Camera/Renderqueue.h" +#include "Runtime/Camera/Camera.h" +#include "Runtime/Camera/Renderable.h" +#include "Runtime/Camera/Light.h" +#include "Runtime/Camera/RenderSettings.h" +#include "Runtime/Camera/RenderManager.h" +#include "Runtime/Camera/Shadows.h" +#include "Runtime/Camera/LODGroupManager.h" +#include "Runtime/Graphics/RenderBufferManager.h" +#include "Runtime/Graphics/GraphicsHelper.h" +#include "Runtime/Graphics/LightmapSettings.h" +#include "Runtime/Graphics/Transform.h" +#include "External/shaderlab/Library/intshader.h" +#include "External/shaderlab/Library/properties.h" +#include "Runtime/Misc/QualitySettings.h" +#include "Runtime/Misc/BuildSettings.h" +#include "Runtime/Shaders/Shader.h" +#include "Runtime/GfxDevice/GfxDevice.h" +#include "Runtime/GfxDevice/BatchRendering.h" +#include "Runtime/Profiler/Profiler.h" +#include "Runtime/Profiler/ExternalGraphicsProfiler.h" +#include "Runtime/Utilities/dynamic_array.h" +#include "BuiltinShaderParamUtility.h" +#include "Runtime/Math/ColorSpaceConversion.h" +#include "Runtime/Camera/LightManager.h" +#include "External/MurmurHash/MurmurHash2.h" + + +// Enable/disable hash based forward shader render loop sorting functionality. +#define ENABLE_FORWARD_SHADER_LOOP_HASH_SORTING 0 + +PROFILER_INFORMATION(gFwdOpaquePrepare, "RenderForwardOpaque.Prepare", kProfilerRender) +PROFILER_INFORMATION(gFwdOpaqueSort, "RenderForwardOpaque.Sort", kProfilerRender) +PROFILER_INFORMATION(gFwdOpaqueCollectShadows, "RenderForwardOpaque.CollectShadows", kProfilerRender) +PROFILER_INFORMATION(gFwdOpaqueRender, "RenderForwardOpaque.Render", kProfilerRender) +PROFILER_INFORMATION(gFwdAlphaPrepare, "RenderForwardAlpha.Prepare", kProfilerRender) +PROFILER_INFORMATION(gFwdAlphaSort, "RenderForwardAlpha.Sort", kProfilerRender) +PROFILER_INFORMATION(gFwdAlphaRender, "RenderForwardAlpha.Render", kProfilerRender) + +static SHADERPROP (ShadowMapTexture); + + +static inline bool CompareLights (ForwardLightsBlock const* a, ForwardLightsBlock const* b) +{ + if (!a || !b) + return false; + + if (a->mainLight != b->mainLight) + return false; + if (a->vertexLightCount != b->vertexLightCount) + return false; + if (a->addLightCount != b->addLightCount) + return false; + + int totalLightCount = a->vertexLightCount + a->addLightCount; + const ActiveLight* const* lightsA = a->GetLights(); + const ActiveLight* const* lightsB = b->GetLights(); + for (int i = 0; i < totalLightCount; ++i) + if (lightsA[i] != lightsB[i]) + return false; + + if (memcmp(a->sh, b->sh, sizeof(a->sh)) != 0) + return false; + + if (!CompareApproximately(a->lastAddLightBlend, b->lastAddLightBlend)) + return false; + if (!CompareApproximately(a->lastVertexLightBlend, b->lastVertexLightBlend)) + return false; + + return true; +} + +struct RenderObjectDataCold { + float invScale; // 4 + float lodFade; // 4 + size_t lightsDataOffset; // 4 into memory block with all light data chunks + int subshaderIndex; // 4 + // 16 bytes +}; + + +namespace ForwardShaderRenderLoop_Enum +{ +// Render pass data here is 8 bytes each; an index of the render object and "the rest" packed +// into 4 bytes. +enum { + kPackPassShift = 0, + kPackPassMask = 0xFF, + kPackTypeShift = 8, + kPackTypeMask = 0xFF, + kPackFirstPassFlag = (1<<24), + kPackMultiPassFlag = (1<<25), +}; + +} // namespace ForwardShaderRenderLoop_Enum + +struct RenderPassData { + int roIndex; + // Packed into UInt32: pass number, pass type, first pass flag, multipass flag + UInt32 data; +#if ENABLE_FORWARD_SHADER_LOOP_HASH_SORTING + // state hash for optimizing render object sorter + UInt32 hash; +#endif +}; +typedef dynamic_array<RenderPassData> RenderPasses; + + +struct ForwardShaderRenderState +{ + int rendererType; + int transformType; + + float invScale; + float lodFade; + + Material* material; + Shader* shader; + int subshaderIndex; + ShaderPassType passType; + int passIndex; + + const ForwardLightsBlock* lights; + int receiveShadows; + + int lightmapIndex; + Vector4f lightmapST; + + UInt32 customPropsHash; + + + void Invalidate() + { + rendererType = -1; + transformType = -1; + invScale = 0.0f; + lodFade = 0.0F; + material = 0; shader = 0; subshaderIndex = -1; passType = kShaderPassTypeCount; passIndex = -1; + lights = 0; + lightmapIndex = -1; lightmapST = Vector4f(0,0,0,0); + receiveShadows = -1; + customPropsHash = 0; + } + + bool operator == (const ForwardShaderRenderState& rhs) const + { + if (this == &rhs) + return true; + + return ( + rendererType == rhs.rendererType && + transformType == rhs.transformType && + material == rhs.material && + shader == rhs.shader && + CompareLights(lights, rhs.lights) && + subshaderIndex == rhs.subshaderIndex && + passType == rhs.passType && + passIndex == rhs.passIndex && + CompareApproximately(invScale,rhs.invScale) && + CompareApproximately(lodFade,rhs.lodFade, LOD_FADE_BATCH_EPSILON) && + #if ENABLE_SHADOWS + receiveShadows == rhs.receiveShadows && + #endif + lightmapIndex == rhs.lightmapIndex && + lightmapST == rhs.lightmapST && + customPropsHash == rhs.customPropsHash + ); + } + + bool operator != (const ForwardShaderRenderState& rhs) const + { + return !(rhs == *this); + } +}; + + +struct ForwardShadowMap +{ + ForwardShadowMap() : light(NULL), texture(NULL) {} + const ActiveLight* light; + RenderTexture* texture; + Matrix4x4f shadowMatrix; + MinMaxAABB receiverBounds; +}; +typedef dynamic_array<ForwardShadowMap> ForwardShadowMaps; + +struct CompactShadowCollectorSortData; + +struct ForwardShaderRenderLoop +{ + const RenderLoopContext* m_Context; + RenderObjectDataContainer* m_Objects; + + dynamic_array<RenderObjectDataCold> m_RenderObjectsCold; + dynamic_array<UInt8> m_RenderObjectsLightData; + + RenderPasses m_PlainRenderPasses; + #if ENABLE_SHADOWS + ForwardShadowMap m_MainShadowMap; + ForwardShadowMaps m_ShadowMaps; + // Render object indices of shadow receivers. + // This includes both shadow receivers and objects that have shadows off, but + // are within shadow distance. They should still participate in screenspace shadow + // gathering, otherwise shadows will be visible through them. + dynamic_array<int> m_ReceiverObjects; + #endif + + BatchRenderer m_BatchRenderer; + + ForwardShaderRenderLoop() + : m_RenderObjectsCold (kMemTempAlloc) + , m_RenderObjectsLightData (kMemTempAlloc) + , m_PlainRenderPasses (kMemTempAlloc) + #if ENABLE_SHADOWS + , m_ShadowMaps (kMemTempAlloc) + , m_ReceiverObjects (kMemTempAlloc) + #endif + { } + + void PerformRendering (const ActiveLight* mainDirShadowLight, RenderTexture* existingShadowMap, const ShadowCullData& shadowCullData, bool disableDynamicBatching, bool sRGBrenderTarget, bool clearFrameBuffer); + #if ENABLE_SHADOWS + RenderTexture* CollectShadows (RenderTexture* inputShadowMap, const Light* light, const Matrix4x4f* shadowMatrices, const float* splitDistances, const Vector4f* splitSphereCentersAndSquaredRadii, bool enableSoftShadows, bool useDualInForward, bool clearFrameBuffer); + void RenderLightShadowMaps (ForwardShadowMap& shadowMap, ShadowCameraData& camData, bool enableSoftShadows, bool useDualInForward, bool clearFrameBuffer); + int SortShadowCollectorsCompact(CompactShadowCollectorSortData* _resultOrder); + #endif + + template <bool opaque> + struct RenderObjectSorter + { + bool operator()( const RenderPassData& ra, const RenderPassData& rb ) const; + const ForwardShaderRenderLoop* queue; + }; + + template <bool opaque> + void SortRenderPassData( RenderPasses& passes ) + { + RenderObjectSorter<opaque> sorter; + sorter.queue = this; + std::sort( passes.begin(), passes.end(), sorter ); + } +}; + + +template <bool opaque> +bool ForwardShaderRenderLoop::RenderObjectSorter<opaque>::operator() (const RenderPassData& ra, const RenderPassData& rb) const +{ + using namespace ForwardShaderRenderLoop_Enum; + + const RenderObjectData& dataa = (*queue->m_Objects)[ra.roIndex]; + const RenderObjectData& datab = (*queue->m_Objects)[rb.roIndex]; + + // Sort by layering depth. + bool globalLayeringResult; + if (CompareGlobalLayeringData(dataa.globalLayeringData, datab.globalLayeringData, globalLayeringResult)) + return globalLayeringResult; + +#if ENABLE_FORWARD_SHADER_LOOP_HASH_SORTING + + if (!opaque) + { + // Sort by render queues first + if( dataa.queueIndex != datab.queueIndex ) + return dataa.queueIndex < datab.queueIndex; + +#if DEBUGMODE + DebugAssertIf (dataa.queueIndex >= kQueueIndexMin && dataa.queueIndex <= kGeometryQueueIndexMax); // this is alpha loop! +#endif + + // Sort strictly by distance unless they are equal + if( dataa.distance != datab.distance ) + return dataa.distance < datab.distance; + } + + UInt64 keya = (0x0000ffff-((dataa.queueIndex)&0x0000ffff))<<16; + UInt64 keyb = (0x0000ffff-((datab.queueIndex)&0x0000ffff))<<16; + + keya |= (ra.data & kPackFirstPassFlag)>>(24-8); + keyb |= (rb.data & kPackFirstPassFlag)>>(24-8); + keya |= (0x000000ff-((dataa.lightmapIndex)&0x000000ff)); + keyb |= (0x000000ff-((datab.lightmapIndex)&0x000000ff)); + keya = keya << 32; + keyb = keyb << 32; + keya |= ra.hash; + keyb |= rb.hash; + + //Sort keys, TODO try to move the key generation outside the sorting loop + if( keya != keyb ) + return (keya > keyb); + +#if DEBUGMODE + if (opaque) + { + DebugAssertIf (dataa.queueIndex < kQueueIndexMin || dataa.queueIndex > kGeometryQueueIndexMax); // this is opaque loop! + } +#endif + + //fall though distance, TODO insert distance into the key + return dataa.distance > datab.distance; + +#else + + // Sort by render queues first + if( dataa.queueIndex != datab.queueIndex ) + return dataa.queueIndex < datab.queueIndex; + +#if DEBUGMODE + if (opaque) { + DebugAssertIf (dataa.queueIndex < kQueueIndexMin || dataa.queueIndex > kGeometryQueueIndexMax); // this is opaque loop! + } else { + DebugAssertIf (dataa.queueIndex >= kQueueIndexMin && dataa.queueIndex <= kGeometryQueueIndexMax); // this is alpha loop! + } +#endif + + if (!opaque) + { + // Sort strictly by distance unless they are equal + if( dataa.distance != datab.distance ) + return dataa.distance < datab.distance; + } + + UInt32 flagsa = ra.data; + UInt32 flagsb = rb.data; + + // render all first passes first + if( (flagsa & kPackFirstPassFlag) != (flagsb & kPackFirstPassFlag) ) + return (flagsa & kPackFirstPassFlag) > (flagsb & kPackFirstPassFlag); + + // sort by lightmap index (fine to do it before source material index + // since every part of same mesh will have the same lightmap index) + if( dataa.lightmapIndex != datab.lightmapIndex ) + return dataa.lightmapIndex < datab.lightmapIndex; + +#if GFX_ENABLE_DRAW_CALL_BATCHING + // if part of predefined static batch, then sort by static batch index + // prefer static batched first as they usually cover quite a lot + if( dataa.staticBatchIndex != datab.staticBatchIndex ) + return dataa.staticBatchIndex > datab.staticBatchIndex; + + // otherwise sort by material index. Some people are using multiple materials + // on a single mesh and expect them to be rendered in order. + if( dataa.staticBatchIndex == 0 && dataa.sourceMaterialIndex != datab.sourceMaterialIndex ) + return dataa.sourceMaterialIndex < datab.sourceMaterialIndex; +#else + // Sort by material index. Some people are using multiple materials + // on a single mesh and expect them to be rendered in order. + if( dataa.sourceMaterialIndex != datab.sourceMaterialIndex ) + return dataa.sourceMaterialIndex < datab.sourceMaterialIndex; +#endif + + // sort by shader + if( dataa.shader != datab.shader ) + return dataa.shader->GetInstanceID() < datab.shader->GetInstanceID(); // just compare instance IDs + + // then sort by material + if( dataa.material != datab.material ) + return dataa.material->GetInstanceID() < datab.material->GetInstanceID(); // just compare instance IDs + + // inside same material: by pass + UInt32 passa = (flagsa >> kPackPassShift) & kPackPassMask; + UInt32 passb = (flagsb >> kPackPassShift) & kPackPassMask; + if( passa != passb ) + return passa < passb; + + if (opaque) + { + // Sort by distance in reverse order. + // That way we get consistency in render order, and more pixels not rendered due to z-testing, + // which benefits performance. + if( dataa.distance != datab.distance ) + return dataa.distance > datab.distance; + } + + // fall through: roIndex + return ra.roIndex < rb.roIndex; + +#endif // ENABLE_FORWARD_SHADER_LOOP_HASH_SORTING +} + +#if ENABLE_SHADOWS +static void SetLightShadowProps (const Camera& camera, const Light& light, Texture* shadowMap, const Matrix4x4f& shadowMatrix, bool useDualInForward) +{ + const float shadowStrength = light.GetShadowStrength(); + DebugAssert (shadowMap); + + ShaderLab::PropertySheet *props = ShaderLab::g_GlobalProperties; + BuiltinShaderParamValues& params = GetGfxDevice().GetBuiltinParamValues(); + + // shadow matrix + CopyMatrix (shadowMatrix.GetPtr(), params.GetWritableMatrixParam(kShaderMatWorldToShadow).GetPtr()); + + props->SetTexture( kSLPropShadowMapTexture, shadowMap ); + + if (light.GetType() == kLightPoint) + { + const Vector3f lightPos = light.GetWorldPosition(); + params.SetVectorParam(kShaderVecLightPositionRange, Vector4f(lightPos.x, lightPos.y, lightPos.z, 1.0f/light.GetRange())); + } + + // ambient & shadow fade out + Vector4f lightFade; + Vector4f fadeCenterAndType; + CalculateLightShadowFade (camera, shadowStrength, lightFade, fadeCenterAndType); + params.SetVectorParam(kShaderVecLightmapFade, lightFade); + if (useDualInForward) + lightFade.z = lightFade.w = 0.0f; + params.SetVectorParam(kShaderVecLightShadowData, lightFade); + params.SetVectorParam(kShaderVecShadowFadeCenterAndType, fadeCenterAndType); + // texel offsets for PCF + Vector4f offsets; + float offX = 0.5f / shadowMap->GetGLWidth(); + float offY = 0.5f / shadowMap->GetGLHeight(); + offsets.z = 0.0f; offsets.w = 0.0f; + offsets.x = -offX; offsets.y = -offY; params.SetVectorParam(kShaderVecShadowOffset0, offsets); + offsets.x = offX; offsets.y = -offY; params.SetVectorParam(kShaderVecShadowOffset1, offsets); + offsets.x = -offX; offsets.y = offY; params.SetVectorParam(kShaderVecShadowOffset2, offsets); + offsets.x = offX; offsets.y = offY; params.SetVectorParam(kShaderVecShadowOffset3, offsets); +} +static void SetLightShadowCollectProps (const Camera& camera, const Light& light, Texture* shadowMap, const Matrix4x4f* shadowMatrices, const float* splitDistances, const Vector4f* splitSphereCentersAndSquaredRadii, bool useDualInForward) +{ + DebugAssert (shadowMatrices && shadowMap); + SetLightShadowProps (camera, light, shadowMap, shadowMatrices[0], useDualInForward); + SetCascadedShadowShaderParams (shadowMatrices, splitDistances, splitSphereCentersAndSquaredRadii); +} +#endif // ENABLE_SHADOWS + + + +void ForwardShaderRenderLoop::PerformRendering (const ActiveLight* mainDirShadowLight, RenderTexture* existingShadowMap, const ShadowCullData& shadowCullData, bool disableDynamicBatching, bool sRGBrenderTarget, bool clearFrameBuffer) +{ + using namespace ForwardShaderRenderLoop_Enum; + + const RenderManager::Renderables& renderables = GetRenderManager ().GetRenderables (); + RenderManager::Renderables::const_iterator renderablesBegin = renderables.begin(), renderablesEnd = renderables.end(); + + SetNoShadowsKeywords(); + + GfxDevice& device = GetGfxDevice(); + // save current scissor params + int oldScissorRect[4]; + device.GetScissorRect(oldScissorRect); + const bool oldScissor = device.IsScissorEnabled(); + + #if ENABLE_SHADOWS + const bool enableSoftShadows = GetSoftShadowsEnabled(); + ShadowCameraData camData(shadowCullData); + ForwardShadowMap mainLightShadowMap; + const bool hasAnyShadows = (mainDirShadowLight != 0 || !m_ShadowMaps.empty()); + const bool useDualInForward = GetLightmapSettings().GetUseDualLightmapsInForward(); + + // shadow map of main directional light + if (mainDirShadowLight != 0) + { + // Render shadow map + if (!existingShadowMap) + { + // Prevent receiver bounds to be zero size in any dimension; + // causes trouble with calculating intersection of frustum and bounds. + mainLightShadowMap.receiverBounds = m_MainShadowMap.receiverBounds; + mainLightShadowMap.receiverBounds.Expand (0.01f); + mainLightShadowMap.light = mainDirShadowLight; + + // One directional light can have shadows in free version, so temporarily + // enable render textures just for that. + RenderTexture::SetTemporarilyAllowIndieRenderTexture (true); + RenderLightShadowMaps (mainLightShadowMap, camData, enableSoftShadows, useDualInForward, clearFrameBuffer); + RenderTexture::SetTemporarilyAllowIndieRenderTexture (false); + + // There were no shadow casters - no shadowmap is produced + if (!mainLightShadowMap.texture) + mainDirShadowLight = 0; + } + else + { + mainLightShadowMap.texture = existingShadowMap; + } + } + + // shadow maps of other lights + for (ForwardShadowMaps::iterator it = m_ShadowMaps.begin(), itEnd = m_ShadowMaps.end(); it != itEnd; ++it) + { + ForwardShadowMap& shadowMap = *it; + + // Prevent receiver bounds to be zero size in any dimension; + // causes trouble with calculating intersection of frustum and bounds. + shadowMap.receiverBounds.Expand (0.01f); + + RenderLightShadowMaps (shadowMap, camData, enableSoftShadows, false, clearFrameBuffer); + } + + if (hasAnyShadows) + { + m_Context->m_Camera->SetupRender (Camera::kRenderFlagSetRenderTarget); + SetNoShadowsKeywords (); + } + #endif + + const RenderSettings& renderSettings = GetRenderSettings(); + const LightmapSettings& lightmapper = GetLightmapSettings(); + size_t npasses = m_PlainRenderPasses.size(); + + int currentQueueIndex = m_Context->m_RenderQueueStart; + + device.SetViewMatrix( m_Context->m_CurCameraMatrix.GetPtr() ); + + ForwardShaderRenderState prevRenderState; + prevRenderState.Invalidate(); + + //If we are in linear lighting enable sRGB writes here... + device.SetSRGBWrite(sRGBrenderTarget); + if (clearFrameBuffer) + m_Context->m_Camera->ClearNoSkybox(false); + else + device.IgnoreNextUnresolveOnCurrentRenderTarget(); + + const ChannelAssigns* channels = NULL; + + for( size_t i = 0; i < npasses; ++i ) + { + const RenderPassData& rpData = m_PlainRenderPasses[i]; + const RenderObjectData& roDataH = (*m_Objects)[rpData.roIndex]; + const RenderObjectDataCold& roDataC = m_RenderObjectsCold[rpData.roIndex]; + const ForwardLightsBlock& roDataL = *reinterpret_cast<ForwardLightsBlock*>(&m_RenderObjectsLightData[roDataC.lightsDataOffset]); + + // We're going over all things that need to be rendered in increasing + // render queue order. Whenever we switch to the new queue, we must + // invoke all "camera renderables" (halos, flares and so on). + const int roQueueIndex = roDataH.queueIndex; + DebugAssert (roQueueIndex >= currentQueueIndex); + if( roQueueIndex > currentQueueIndex ) + { + m_BatchRenderer.Flush(); + + // Draw required renderables + if (!m_Context->m_DontRenderRenderables) + { + while( renderablesBegin != renderablesEnd && renderablesBegin->first <= roQueueIndex ) + { + renderablesBegin->second->RenderRenderable(*m_Context->m_CullResults); + ++renderablesBegin; + } + } + + currentQueueIndex = roQueueIndex; + } + + const VisibleNode *node = roDataH.visibleNode; + const UInt16 subsetIndex = roDataH.subsetIndex; + + ForwardShaderRenderState rs; + { + rs.rendererType = node->renderer->GetRendererType(); + rs.transformType = node->transformType; + rs.invScale = roDataC.invScale; + rs.lodFade = roDataC.lodFade; + + rs.material = roDataH.material; + rs.shader = roDataH.shader; + rs.subshaderIndex = roDataC.subshaderIndex; + rs.passType = (ShaderPassType)((rpData.data >> kPackTypeShift) & kPackTypeMask); + rs.passIndex = (rpData.data >> kPackPassShift) & kPackPassMask; + + rs.lights = &roDataL; + #if ENABLE_SHADOWS + rs.receiveShadows = hasAnyShadows && node->renderer->GetReceiveShadows() && IsObjectWithinShadowRange (*m_Context->m_ShadowCullData, node->worldAABB); + #endif + + rs.lightmapIndex = roDataH.lightmapIndex; + DebugAssert(rs.lightmapIndex == node->renderer->GetLightmapIndex()); + rs.lightmapST = node->renderer->GetLightmapSTForRendering(); + rs.customPropsHash = node->renderer->GetCustomPropertiesHash(); + } + + + // multi-pass requires vertex position values to be EXACTLY the same for all passes + // therefore do NOT batch dynamic multi-pass nodes + // same for shadow casters + const bool multiPass = (rpData.data & kPackMultiPassFlag) == kPackMultiPassFlag; + const bool dynamicShouldNotBatch = (node->renderer->GetStaticBatchIndex() == 0) && (multiPass || disableDynamicBatching); + + #if ENABLE_SHADOWS + const bool dynamicAndShadowCaster = (node->renderer->GetStaticBatchIndex() == 0) && (mainDirShadowLight != 0) && node->renderer->GetCastShadows(); + #else + const bool dynamicAndShadowCaster = false; + #endif + + bool shouldResetPass; + if (rs.passType == kPassForwardAdd || // rendering multiple different lights in a row - impossible to batch + prevRenderState != rs) + { + // break the batch + m_BatchRenderer.Flush(); + prevRenderState = rs; + shouldResetPass = true; + } + // We can not use dynamic batching for shadow casting renderers or multipass renderers, + // because that will lead to zfighting due to slightly different vertex positions + else if (dynamicAndShadowCaster || dynamicShouldNotBatch) + { + m_BatchRenderer.Flush(); + shouldResetPass = false; + } + else + shouldResetPass = false; + + renderSettings.SetupAmbient(); + SetObjectScale(device, roDataC.lodFade, roDataC.invScale); + + node->renderer->ApplyCustomProperties(*roDataH.material, rs.shader, rs.subshaderIndex); + + // non batchable and generally inefficient multi-pass path + if (rs.passType == kPassForwardAdd) + { + const int lightCount = rs.lights->addLightCount; + const ActiveLight* const* addLights = rs.lights->GetLights(); + for( int lightNo = 0; lightNo < lightCount; ++lightNo ) + { + const ActiveLight& activeLight = *addLights[lightNo]; + Light* light = activeLight.light; + LightManager::SetupForwardAddLight (light, lightNo==lightCount-1 ? rs.lights->lastAddLightBlend : 1.0f); + + if (light->GetType() != kLightDirectional) + SetLightScissorRect (activeLight.screenRect, m_Context->m_CameraViewport, false, device); + + + #if ENABLE_SHADOWS + if (rs.receiveShadows && light->GetShadows() != kShadowNone) + { + // find light among additional shadow lights + ForwardShadowMaps::iterator sl, slEnd = m_ShadowMaps.end(); + for (sl = m_ShadowMaps.begin(); sl != slEnd; ++sl) + { + if (sl->light == &activeLight && sl->texture) + { + const Light& light = *activeLight.light; + SetLightShadowProps (*m_Context->m_Camera, light, sl->texture, sl->shadowMatrix, false); + SetShadowsKeywords (light.GetType(), light.GetShadows(), light.GetType()==kLightDirectional, enableSoftShadows); + break; + } + } + } + #endif + + channels = rs.material->SetPassWithShader(rs.passIndex, rs.shader, rs.subshaderIndex); + if (channels) + { + SetupObjectMatrix (node->worldMatrix, rs.transformType); + node->renderer->Render( subsetIndex, *channels ); + } + + #if ENABLE_SHADOWS + if (rs.receiveShadows && light->GetShadows() != kShadowNone) + { + SetNoShadowsKeywords (); + } + #endif + + if (light->GetType() != kLightDirectional) + ClearScissorRect (oldScissor, oldScissorRect, device); + } + } + else + { + // only setup lights & pass state when they're differ from previous + if (shouldResetPass) + { + // only setup lights & pass state when they're differ from previous + switch( rs.passType ) + { + case kPassAlways: + { + // Disable all fixed function lights for consistency (so if user + // has accidentally Lighting On in an Always pass, it will not produce + // random results) + device.DisableLights (0); + + // Reset SH lighting + float blackSH [9][3]; + memset (blackSH, 0, (9 * 3)* sizeof(float)); + SetSHConstants (blackSH, GetGfxDevice().GetBuiltinParamValues()); + + SetupObjectLightmaps (lightmapper, rs.lightmapIndex, rs.lightmapST, false); + } + break; + + case kPassForwardBase: + { + // NOTE: identity matrix has to be set for GLSL & OpenGLES before vertex lights are set + // as lighting is specified in World space + device.SetWorldMatrix( Matrix4x4f::identity.GetPtr() ); + + LightManager::SetupForwardBaseLights (*rs.lights); + SetupObjectLightmaps (lightmapper, rs.lightmapIndex, rs.lightmapST, false); + + #if ENABLE_SHADOWS + if (rs.receiveShadows && mainDirShadowLight && rs.lights->mainLight == mainDirShadowLight) + { + const Light& light = *mainDirShadowLight->light; + SetLightShadowProps (*m_Context->m_Camera, light, mainLightShadowMap.texture, mainLightShadowMap.shadowMatrix, false); + SetShadowsKeywords (light.GetType(), light.GetShadows(), true, enableSoftShadows); + } + #endif + } + break; + + case kPassVertex: + case kPassVertexLM: + case kPassVertexLMRGBM: + { + // NOTE: identity matrix has to be set for GLSL & OpenGLES before vertex lights are set + // as lighting is specified in World space + device.SetWorldMatrix( Matrix4x4f::identity.GetPtr() ); + + SetupObjectLightmaps (lightmapper, rs.lightmapIndex, rs.lightmapST, true); + LightManager::SetupVertexLights( rs.lights->vertexLightCount, rs.lights->GetLights() ); + } + break; + + default: + { + AssertString ("This pass type should not happen"); + break; + } + } + + channels = roDataH.material->SetPassWithShader(rs.passIndex, rs.shader, rs.subshaderIndex); + } + + if (channels) + m_BatchRenderer.Add(node->renderer, subsetIndex, channels, node->worldMatrix, rs.transformType); + + if (ENABLE_SHADOWS && rs.passType == kPassForwardBase) + SetNoShadowsKeywords (); + } + } + + m_BatchRenderer.Flush(); + + SetNoShadowsKeywords (); + + // restore scissor + ClearScissorRect (oldScissor, oldScissorRect, device); + + #if ENABLE_SHADOWS + if (mainLightShadowMap.texture && mainLightShadowMap.texture != existingShadowMap) + GetRenderBufferManager().ReleaseTempBuffer( mainLightShadowMap.texture ); + for (ForwardShadowMaps::iterator it = m_ShadowMaps.begin(), itEnd = m_ShadowMaps.end(); it != itEnd; ++it) + { + ForwardShadowMap& sl = *it; + if (sl.texture) + GetRenderBufferManager().ReleaseTempBuffer (sl.texture); + } + #endif + + // After everything we might still have renderables that should be drawn and the + // very end. Do it. + if (!m_Context->m_DontRenderRenderables) + { + while (renderablesBegin != renderablesEnd && renderablesBegin->first < m_Context->m_RenderQueueStart) + ++renderablesBegin; + while( renderablesBegin != renderablesEnd && renderablesBegin->first < m_Context->m_RenderQueueEnd ) + { + renderablesBegin->second->RenderRenderable(*m_Context->m_CullResults); + ++renderablesBegin; + } + } + GetGfxDevice().SetSRGBWrite(false); + device.SetViewMatrix( m_Context->m_CurCameraMatrix.GetPtr() ); +} + + +// ------------------------------------------------------------------------ +// collect cascaded shadows into screen-space texture; apply blur + +#if ENABLE_SHADOWS + +struct ShadowCollectorSorter +{ + bool operator() (int raIndex, int rbIndex) const; + const ForwardShaderRenderLoop* queue; +}; + +bool ShadowCollectorSorter::operator()(int raIndex, int rbIndex) const +{ + const RenderObjectData& ra = (*queue->m_Objects)[raIndex]; + const RenderObjectData& rb = (*queue->m_Objects)[rbIndex]; + + // Sort by layering depth. //@TODO:should this be here? + bool globalLayeringResult; + if (CompareGlobalLayeringData(ra.globalLayeringData, rb.globalLayeringData, globalLayeringResult)) + return globalLayeringResult; + + // Sort front to back + return ra.distance > rb.distance; +} + +struct CompactShadowCollectorSortData +{ + UInt64 key; // 64b key, stores full 32b material instance ID, 16b internal static batch ID, 2b for transform type, and 14b depth + int collectorIndex; + + CompactShadowCollectorSortData(UInt32 _smallMeshIndex, UInt32 _instanceID, TransformType _transformType, float _depth, int _collectorIndex ) + { + key=0; + UInt32 transformType = static_cast<UInt32>(_transformType); + UInt32 z = (UInt32)(16383.0f*_depth); + + key |= (_instanceID); + key = key << 32; + key |= ((_smallMeshIndex&0x0000ffff)<<16)|((transformType&0x00000003)<<14)|(z&0x00003fff); + + collectorIndex = _collectorIndex; + } +}; + +struct CompactShadowCollectorKeySorter +{ + inline bool operator()(const CompactShadowCollectorSortData& a, const CompactShadowCollectorSortData& b) + { + return a.key < b.key; + } +}; + +// Shadow collector sorting +// Sorted shadow collector order is stored into m_ReceiverObjects +// Output: +// _resultOrder - Sorted shadow caster sort data +// Returns: +// Number of active collectors +int ForwardShaderRenderLoop::SortShadowCollectorsCompact(CompactShadowCollectorSortData* _resultOrder) +{ + int activeShadowCollectors = 0; + + // Generate key array for sorting + for( int i = 0; i < m_ReceiverObjects.size(); ++i ) + { + int roIndex = m_ReceiverObjects[i]; + const RenderObjectData& roDataH = (*m_Objects)[roIndex]; + Shader* shader = roDataH.shader; + + if( shader->HasShadowCollectorPass() ) + { + const TransformInfo& xformInfo = roDataH.visibleNode->renderer->GetTransformInfo(); + + Matrix4x4f worldToClipMatrix = m_Context->m_Camera->GetWorldToClipMatrix(); + const Vector3f& worldPos = roDataH.visibleNode->worldAABB.GetCenter(); + float z = worldToClipMatrix.Get (2, 0) * worldPos.x + worldToClipMatrix.Get (2, 1) * worldPos.y + worldToClipMatrix.Get (2, 2) * worldPos.z + worldToClipMatrix.Get (2, 3); + float w = worldToClipMatrix.Get (3, 0) * worldPos.x + worldToClipMatrix.Get (3, 1) * worldPos.y + worldToClipMatrix.Get (3, 2) * worldPos.z + worldToClipMatrix.Get (3, 3); + float z_proj = z/w; + z_proj = max(z_proj,0.0f); + z_proj = min(z_proj,1.0f); + + _resultOrder[activeShadowCollectors++] = CompactShadowCollectorSortData( roDataH.visibleNode->renderer->GetMeshIDSmall(), roDataH.material->GetShadowCollectorHash(), + xformInfo.transformType, z_proj, roIndex ); + + } + } + + std::sort( _resultOrder, _resultOrder + activeShadowCollectors, CompactShadowCollectorKeySorter() ); + + return activeShadowCollectors; +} + +RenderTexture* ForwardShaderRenderLoop::CollectShadows (RenderTexture* inputShadowMap, const Light* light, const Matrix4x4f* shadowMatrices, const float* splitDistances, const Vector4f* splitSphereCentersAndSquaredRadii, bool enableSoftShadows, bool useDualInForward, bool clearFrameBuffer) +{ + PROFILER_AUTO_GFX(gFwdOpaqueCollectShadows, m_Context->m_Camera) + GPU_AUTO_SECTION(kGPUSectionShadowPass); + + DebugAssert (shadowMatrices && inputShadowMap && light && splitDistances); + + //Sort shadow collectors +#if GFX_ENABLE_SHADOW_BATCHING + CompactShadowCollectorSortData* sortOrder; + ALLOC_TEMP(sortOrder, CompactShadowCollectorSortData, m_ReceiverObjects.size()); + int shadowColectors = SortShadowCollectorsCompact(sortOrder); +#else + ShadowCollectorSorter sorter; + sorter.queue = this; + std::sort (m_ReceiverObjects.begin(), m_ReceiverObjects.end(), sorter); +#endif + + // If camera is rendering into a texture, we can share its depth buffer while collecting shadows. + // This doesn't apply if the target texture is antialiased (case 559079). + bool shareDepthBuffer = false; + RenderTexture* cameraRT = m_Context->m_Camera->GetCurrentTargetTexture(); + if (cameraRT && cameraRT->GetDepthFormat() != kDepthFormatNone && !cameraRT->IsAntiAliased()) + { + shareDepthBuffer = true; + if (!cameraRT->IsCreated()) + cameraRT->Create(); + } + + // create screen-space render texture and collect shadows into it + RenderTexture* screenShadowMap = GetRenderBufferManager().GetTempBuffer (RenderBufferManager::kFullSize, RenderBufferManager::kFullSize, shareDepthBuffer ? kDepthFormatNone : kDepthFormat24, kRTFormatARGB32, 0, kRTReadWriteLinear); + if (shareDepthBuffer) + { + if (!screenShadowMap->IsCreated()) + screenShadowMap->Create(); + RenderSurfaceHandle rtSurfaceColor = screenShadowMap->GetColorSurfaceHandle(); + RenderSurfaceHandle rtSurfaceDepth = cameraRT->GetDepthSurfaceHandle(); + RenderTexture::SetActive (1, &rtSurfaceColor, rtSurfaceDepth, screenShadowMap); + } + else + { + RenderTexture::SetActive (screenShadowMap); + } + + GfxDevice& device = GetGfxDevice(); + // (case 555375) + // Clear all, expect in cases where depth buffer is shared and forward path is used to render deferred path shadow receiving objects + // (clearFrameBuffer variable is false in those cases) + bool clearColorOnly = shareDepthBuffer && !clearFrameBuffer; + device.Clear (clearColorOnly ? kGfxClearColor : kGfxClearAll, ColorRGBAf(1,1,1,0).GetPtr(), 1.0f, 0); + if (clearColorOnly) + device.IgnoreNextUnresolveOnCurrentRenderTarget(); + GPU_TIMESTAMP(); + m_Context->m_Camera->SetupRender (); + + SetLightShadowCollectProps (*m_Context->m_Camera, *light, inputShadowMap, shadowMatrices, splitDistances, splitSphereCentersAndSquaredRadii, useDualInForward); + light->SetPropsToShaderLab (1.0f); + + device.SetViewMatrix( m_Context->m_CurCameraMatrix.GetPtr() ); + +#if GFX_ENABLE_SHADOW_BATCHING + + device.SetInverseScale(1.0f); + m_BatchRenderer.Flush(); + + if (shadowColectors > 0) + { + UInt64 previousKey = ((sortOrder[0].key)&0xFFFFFFFFFFFFC000ULL); // depth component does not affect state change boundaries + UInt32 previousHash = 0; + int roIndex = sortOrder[0].collectorIndex; + const ChannelAssigns* channels = (*m_Objects)[roIndex].material->SetShadowCollectorPassWithShader ((*m_Objects)[roIndex].shader, m_RenderObjectsCold[roIndex].subshaderIndex); + + for(int i=0; i<shadowColectors;i++) + { + UInt64 currentKey = ((sortOrder[i].key)&0xFFFFFFFFFFFFC000ULL); + + roIndex = sortOrder[i].collectorIndex; + const RenderObjectData& roDataH = (*m_Objects)[roIndex]; + Shader* shader = roDataH.shader; + const TransformInfo& xformInfo = roDataH.visibleNode->renderer->GetTransformInfo (); + const RenderObjectDataCold& roDataC = m_RenderObjectsCold[roIndex]; + + roDataH.visibleNode->renderer->ApplyCustomProperties(*roDataH.material, shader, roDataC.subshaderIndex); + + UInt32 currentHash = roDataH.visibleNode->renderer->GetCustomPropertiesHash(); + + // different property hasah or shared depth buffer cause Flush(), state setup, and one non-batched draw call + if (currentHash != previousHash || shareDepthBuffer) + { + m_BatchRenderer.Flush(); // empty BatchRenderer + channels = roDataH.material->SetShadowCollectorPassWithShader(shader, roDataC.subshaderIndex); + SetupObjectMatrix(xformInfo.worldMatrix, xformInfo.transformType); + roDataH.visibleNode->renderer->Render( roDataH.subsetIndex, *channels ); + } + else + { + if (previousKey != currentKey) // Flush() and update state when key changes + { + m_BatchRenderer.Flush(); + channels = roDataH.material->SetShadowCollectorPassWithShader (shader, roDataC.subshaderIndex); + } + + // if this pass needs to be rendered + if (channels) + m_BatchRenderer.Add(roDataH.visibleNode->renderer, roDataH.subsetIndex, channels, xformInfo.worldMatrix, xformInfo.transformType); + } + previousKey = currentKey; + previousHash = currentHash; + } + m_BatchRenderer.Flush(); + } + +#else // GFX_ENABLE_SHADOW_BATCHING + + size_t npasses = m_ReceiverObjects.size(); + for( size_t i = 0; i < npasses; ++i ) + { + int roIndex = m_ReceiverObjects[i]; + const RenderObjectData& roDataH = (*m_Objects)[roIndex]; + const RenderObjectDataCold& roDataC = m_RenderObjectsCold[roIndex]; + + Shader* shader = roDataH.shader; + if( !shader->HasShadowCollectorPass() ) + continue; + + const VisibleNode* node = roDataH.visibleNode; + BaseRenderer* renderer = node->renderer; + SetObjectScale(device, roDataC.lodFade, roDataC.invScale); + + renderer->ApplyCustomProperties(*roDataH.material, shader, roDataC.subshaderIndex); + + const ChannelAssigns* channels = roDataH.material->SetShadowCollectorPassWithShader(shader, roDataC.subshaderIndex); + SetupObjectMatrix (node->worldMatrix, node->transformType); + renderer->Render( roDataH.subsetIndex, *channels ); + } + +#endif // GFX_ENABLE_SHADOW_BATCHING + + GetRenderBufferManager().ReleaseTempBuffer( inputShadowMap ); + + // + // possibly blur into another screen-space render texture + + if( IsSoftShadow(light->GetShadows()) && enableSoftShadows ) + { + return BlurScreenShadowMap (screenShadowMap, light->GetShadows(), m_Context->m_Camera->GetFar(), light->GetShadowSoftness(), light->GetShadowSoftnessFade()); + } + + return screenShadowMap; +} +#endif // ENABLE_SHADOWS + +// ------------------------------------------------------------------------ +// render shadow maps for a single light + + +#if ENABLE_SHADOWS + +void ForwardShaderRenderLoop::RenderLightShadowMaps (ForwardShadowMap& shadowMap, ShadowCameraData& camData, bool enableSoftShadows, bool useDualInForward, bool clearFrameBuffer) +{ + // Set correct keywords before rendering casters (caster passes use keywords for shader selection) + const Light* light = shadowMap.light->light; + SetShadowsKeywords( light->GetType(), light->GetShadows(), false, enableSoftShadows ); + GetGfxDevice().SetViewMatrix( m_Context->m_CurCameraMatrix.GetPtr() ); + + Matrix4x4f shadowMatrices[kMaxShadowCascades]; + +#if UNITY_EDITOR + bool useLightmaps = GetLightmapVisualization ().GetUseLightmapsForRendering (); +#else + bool useLightmaps = true; +#endif + + bool excludeLightmapped = !useDualInForward && useLightmaps; + shadowMap.texture = RenderShadowMaps (camData, *shadowMap.light, shadowMap.receiverBounds, excludeLightmapped, shadowMatrices); + CopyMatrix (shadowMatrices[0].GetPtr(), shadowMap.shadowMatrix.GetPtr()); + + // Shadow map can be null if out of memory; or no shadow casters present + if (gGraphicsCaps.hasShadowCollectorPass && shadowMap.texture && light->GetType() == kLightDirectional) + { + SetShadowsKeywords( light->GetType(), light->GetShadows(), false, enableSoftShadows ); + shadowMap.texture = CollectShadows (shadowMap.texture, light, shadowMatrices, camData.splitDistances, camData.splitSphereCentersAndSquaredRadii, enableSoftShadows, useDualInForward, clearFrameBuffer); + } + else + { + // If shadow map could not actually be created (out of VRAM, whatever), set the no shadows + // keywords and proceed. So there will be no shadows, but otherwise it will be ok. + SetNoShadowsKeywords(); + } +} + +#endif // ENABLE_SHADOWS + + +// ------------------------------------------------------------------------ +// rendering entry points + +ForwardShaderRenderLoop* CreateForwardShaderRenderLoop() +{ + return new ForwardShaderRenderLoop(); +} + +void DeleteForwardShaderRenderLoop (ForwardShaderRenderLoop* queue) +{ + delete queue; +} + +static bool IsPassSuitable (UInt32 currentRenderOptions, UInt32 passRenderOptions, ShaderPassType passType, + bool isLightmapped, bool useRGBM, bool useVertexLights, bool hasAddLights) +{ + // All options that a pass requires must be on + if( (currentRenderOptions & passRenderOptions) != passRenderOptions ) + return false; // some options are off, skip this pass + + if (useVertexLights) + { + if (passType != kPassAlways && passType != kPassVertex && + passType != kPassVertexLM && passType != kPassVertexLMRGBM) + return false; + + // Use either lightmapped or non-lightmapped pass + if ((passType == kPassVertex && isLightmapped) || + ((passType == kPassVertexLM || passType == kPassVertexLMRGBM) && !isLightmapped)) + return false; + + // Use pass that can properly decode the lightmap + if ((passType == kPassVertexLM && useRGBM) || + (passType == kPassVertexLMRGBM && !useRGBM)) + return false; + } + else + { + if (passType != kPassAlways && passType != kPassForwardBase && passType != kPassForwardAdd) + return false; // pass does not belong to forward loop + + if (!hasAddLights && passType == kPassForwardAdd) + return false; // additive pass but have no additive lights + } + return true; +} + +// A point or spot light might be completely behind shadow distance, +// so there's no point in doing shadows on them. +static bool IsLightBeyondShadowDistance (const Light& light, const Matrix4x4f& cameraMatrix, float shadowDistance) +{ + if (light.GetType() == kLightDirectional) + return false; + const Vector3f lightPos = light.GetComponent(Transform).GetPosition(); + float distanceToLight = -cameraMatrix.MultiplyPoint3 (lightPos).z; + if (distanceToLight - light.GetRange() > shadowDistance) + return true; + return false; +} + + +static void PutAdditionalShadowLights (const AABB& bounds, ForwardLightsBlock& lights, const Matrix4x4f& cameraMatrix, float shadowDistance, ForwardShadowMaps& outShadowMaps) +{ + const int lightCount = lights.addLightCount; + const ActiveLight* const* addLights = lights.GetLights(); + for (int lightNo = 0; lightNo < lightCount; ++lightNo) + { + const ActiveLight* light = addLights[lightNo]; + if (light->light->GetShadows() == kShadowNone) + continue; + + // Find this light's shadow data + ForwardShadowMaps::iterator sl, slEnd = outShadowMaps.end(); + ForwardShadowMap* found = NULL; + for (sl = outShadowMaps.begin(); sl != slEnd; ++sl) + { + if (sl->light == light) + { + found = &(*sl); + break; + } + } + if (sl == slEnd) + { + // Point/Spot light beyond shadow distance: no need to add + if (IsLightBeyondShadowDistance (*light->light, cameraMatrix, shadowDistance)) + continue; + + ForwardShadowMap& shadowMap = outShadowMaps.push_back (); + shadowMap.light = light; + shadowMap.receiverBounds = bounds; + shadowMap.texture = NULL; + } + else + { + found->receiverBounds.Encapsulate (bounds); + } + } +} + +#if ENABLE_FORWARD_SHADER_LOOP_HASH_SORTING +template<typename T> +static UInt8* InsertIntoHashBuffer(const T* p, UInt8* buffer) +{ + Assert((sizeof(T) % 4) == 0); // unaligned write + *reinterpret_cast<T*>(buffer) = *p; + return buffer + sizeof(T); +} +#endif + +void DoForwardShaderRenderLoop ( + RenderLoopContext& ctx, + RenderObjectDataContainer& objects, + bool opaque, + bool disableDynamicBatching, + RenderTexture* mainShadowMap, + ActiveLights& activeLights, + bool linearLighting, + bool clearFrameBuffer) +{ + GPU_AUTO_SECTION(opaque ? kGPUSectionOpaquePass : kGPUSectionTransparentPass); + using namespace ForwardShaderRenderLoop_Enum; + + const QualitySettings::QualitySetting& quality = GetQualitySettings().GetCurrent(); + + // figure out hardware supports shadows + #if ENABLE_SHADOWS + float shadowDistance = QualitySettings::GetShadowDistanceForRendering(); + const bool receiveShadows = + opaque && + GetBuildSettings().hasShadows && + CheckPlatformSupportsShadows() && + (quality.shadows != QualitySettings::kShadowsDisable) && + (shadowDistance > 0.0f); + const bool localLightShadows = receiveShadows && GetBuildSettings().hasLocalLightShadows; + #endif + + bool useRGBM = gGraphicsCaps.SupportsRGBM(); + + // Allocated on the stack each time, uses temp allocators + ForwardShaderRenderLoop queue; + queue.m_Context = &ctx; + queue.m_Objects = &objects; + queue.m_RenderObjectsCold.reserve(objects.size()); + const int kEstimatedLightDataPerObject = sizeof(ForwardLightsBlock) + kEstimatedLightsPerObject * sizeof(Light*); + queue.m_RenderObjectsLightData.reserve(objects.size() * kEstimatedLightDataPerObject); + + const ActiveLight* mainDirShadowLight = NULL; + + // figure out current rendering options + UInt32 currentRenderOptions = GetCurrentRenderOptions (); + + RenderSettings& renderSettings = GetRenderSettings(); + LightManager& lightManager = GetLightManager(); + const int pixelLightCount = quality.pixelLightCount; + const bool dualLightmapsMode = (GetLightmapSettings().GetLightmapsMode() == LightmapSettings::kDualLightmapsMode); + +#if UNITY_EDITOR + bool useLightmaps = GetLightmapVisualization ().GetUseLightmapsForRendering (); +#endif + + const CullResults& cullResults = *ctx.m_CullResults; + + // Figure everything out + { + PROFILER_AUTO((opaque?gFwdOpaquePrepare:gFwdAlphaPrepare), ctx.m_Camera); + + RenderObjectDataContainer::iterator itEnd = objects.end(); + size_t roIndex = 0; + for (RenderObjectDataContainer::iterator it = objects.begin(); it != itEnd; ++it, ++roIndex) + { + RenderObjectData& odata = *it; + const VisibleNode *node = odata.visibleNode; + size_t visibleNodeIndex = node - cullResults.nodes.begin(); + + BaseRenderer* renderer = node->renderer; + +#if UNITY_EDITOR + const bool isLightmapped = renderer->IsLightmappedForRendering() && useLightmaps; +#else + const bool isLightmapped = renderer->IsLightmappedForRendering(); +#endif + + ShaderLab::IntShader& slshader = *odata.shader->GetShaderLabShader(); + RenderObjectDataCold& roDataC = queue.m_RenderObjectsCold.push_back(); + bool useVertexLights = false; + if (odata.subShaderIndex == -1) + { + int ss = slshader.GetDefaultSubshaderIndex (kRenderPathExtForward); + if (ss == -1) + { + ss = slshader.GetDefaultSubshaderIndex (isLightmapped ? kRenderPathExtVertexLM : kRenderPathExtVertex); + useVertexLights = true; + } + if (ss == -1) + continue; + roDataC.subshaderIndex = ss; + } + else + { + roDataC.subshaderIndex = odata.subShaderIndex; + } + ShaderLab::SubShader& subshader = slshader.GetSubShader(roDataC.subshaderIndex); + + bool disableAddLights = false; + if (!useVertexLights) + { + // If we only have ForwardBase pass and no ForwardAdd, + // disable additive lights completely. Only support main directional, + // vertex & SH. + disableAddLights = !subshader.GetSupportsForwardAddLights(); + } + + size_t objectLightsOffset = queue.m_RenderObjectsLightData.size(); + roDataC.lightsDataOffset = objectLightsOffset; + + lightManager.FindForwardLightsForObject ( + queue.m_RenderObjectsLightData, + GetObjectLightIndices(cullResults, visibleNodeIndex), + GetObjectLightCount(cullResults, visibleNodeIndex), + activeLights, + *node, + isLightmapped, + dualLightmapsMode, + useVertexLights, + pixelLightCount, + disableAddLights, + renderSettings.GetAmbientLightInActiveColorSpace()); + + ForwardLightsBlock& roDataL = *reinterpret_cast<ForwardLightsBlock*>(&queue.m_RenderObjectsLightData[objectLightsOffset]); + const bool hasAddLights = (roDataL.addLightCount != 0); + + #if ENABLE_SHADOWS + bool objectReceivesShadows = renderer->GetReceiveShadows(); + bool withinShadowDistance = IsObjectWithinShadowRange (*ctx.m_ShadowCullData, node->worldAABB); + if (receiveShadows && withinShadowDistance) + { + queue.m_ReceiverObjects.push_back (roIndex); + + if (objectReceivesShadows) + { + // deal with main directional shadow light + if (roDataL.mainLight && roDataL.mainLight->light->GetShadows() != kShadowNone) + { + if (!mainDirShadowLight) + mainDirShadowLight = roDataL.mainLight; + if (mainDirShadowLight == roDataL.mainLight) + queue.m_MainShadowMap.receiverBounds.Encapsulate (node->worldAABB); + } + + // deal with additive shadow lights if needed + if (localLightShadows && subshader.GetSupportsFullForwardShadows()) + { + PutAdditionalShadowLights (node->worldAABB, roDataL, ctx.m_CurCameraMatrix, shadowDistance, queue.m_ShadowMaps); + } + } + } + #endif + + roDataC.invScale = node->invScale; + roDataC.lodFade = node->lodFade; + + int shaderPassCount = subshader.GetValidPassCount(); + + // Determine if we will need more than a single pass + int suitablePasses = 0; + for( int pass = 0; pass < shaderPassCount && suitablePasses < 2; ++pass ) + { + ShaderPassType passType; UInt32 passRenderOptions; + subshader.GetPass(pass)->GetPassOptions( passType, passRenderOptions ); + + if (IsPassSuitable (currentRenderOptions, passRenderOptions, passType, isLightmapped, useRGBM, useVertexLights, hasAddLights)) + ++suitablePasses; + } + + // Go over all passes in the shader + UInt32 firstPassFlag = kPackFirstPassFlag; + const UInt32 multiPassFlag = (suitablePasses > 1)? kPackMultiPassFlag: 0; + for( int pass = 0; pass < shaderPassCount; ++pass ) + { + ShaderPassType passType; UInt32 passRenderOptions; + subshader.GetPass(pass)->GetPassOptions( passType, passRenderOptions ); + + if (!IsPassSuitable (currentRenderOptions, passRenderOptions, passType, isLightmapped, useRGBM, useVertexLights, hasAddLights)) + continue; // skip this pass + + RenderPassData rpData; + rpData.roIndex = roIndex; + rpData.data = + ((pass & kPackPassMask) << kPackPassShift) | + (passType << kPackTypeShift) | + firstPassFlag | + multiPassFlag; + +#if ENABLE_FORWARD_SHADER_LOOP_HASH_SORTING + + //hash state information for render object sorter + const int kHashBufferSize = 64; + UInt8 hashBuffer[kHashBufferSize]; + UInt8* hashPtr = hashBuffer; + + // Always write 32b granularity into the hash buffer to avoid unaligned writes + if (opaque) + hashPtr = InsertIntoHashBuffer(&node->invScale, hashPtr); + int materialID = odata.material->GetInstanceID(); + hashPtr = InsertIntoHashBuffer(&materialID, hashPtr); + hashPtr = InsertIntoHashBuffer(&roDataC.subshaderIndex, hashPtr); + UInt32 shaderPassType = (ShaderPassType)((rpData.data >> kPackTypeShift) & kPackTypeMask); + hashPtr = InsertIntoHashBuffer(&shaderPassType, hashPtr); + UInt32 passIndex = (rpData.data >> kPackPassShift) & kPackPassMask; + hashPtr = InsertIntoHashBuffer(&passIndex, hashPtr); +#if GFX_ENABLE_DRAW_CALL_BATCHING + hashPtr = InsertIntoHashBuffer(&odata.staticBatchIndex, hashPtr); +#endif + Assert(hashPtr-hashBuffer <= kHashBufferSize); + + rpData.hash = MurmurHash2A(hashBuffer, hashPtr-hashBuffer, 0x9747b28c); +#endif + queue.m_PlainRenderPasses.push_back( rpData ); + + firstPassFlag = 0; + } + } + } + + // sort everything + { + PROFILER_AUTO((opaque?gFwdOpaqueSort:gFwdAlphaSort), ctx.m_Camera); + if (opaque) + queue.SortRenderPassData<true> (queue.m_PlainRenderPasses); + else + queue.SortRenderPassData<false> (queue.m_PlainRenderPasses); + } + + // Render everything. When transitioning to render queues, + // it will invoke camera renderables (halos, and so on) + { + PROFILER_AUTO_GFX((opaque?gFwdOpaqueRender:gFwdAlphaRender), ctx.m_Camera); + RenderTexture* rtMain = ctx.m_Camera->GetCurrentTargetTexture (); + queue.PerformRendering (mainDirShadowLight, mainShadowMap, *ctx.m_ShadowCullData, disableDynamicBatching, linearLighting && (!rtMain || rtMain->GetSRGBReadWrite()), clearFrameBuffer); + } +} |