void VegetationSpawnManager::Render( Metal::DeviceContext& context, LightingParserContext& parserContext, unsigned techniqueIndex, RenderCore::Assets::DelayStep delayStep) { if (_pimpl->_cfg._objectTypes.empty()) return; _pimpl->FillInDrawCallSets(); auto& sharedStates = _pimpl->_modelCache->GetSharedStateSet(); auto captureMarker = sharedStates.CaptureState( context, parserContext.GetStateSetResolver(), parserContext.GetStateSetEnvironment()); auto& state = parserContext.GetTechniqueContext()._runtimeState; state.SetParameter(u("SPAWNED_INSTANCE"), 1); auto cleanup = MakeAutoCleanup( [&state]() { state.SetParameter(u("SPAWNED_INSTANCE"), 0); }); auto& resources = *_pimpl->_resources; for (unsigned b=0; b<unsigned(_pimpl->_drawCallSets.size()); ++b) ModelRenderer::RenderPrepared( RenderCore::Assets::ModelRendererContext(context, parserContext, techniqueIndex), sharedStates, _pimpl->_drawCallSets[b], delayStep, [&context, b, &resources](ModelRenderer::DrawCallEvent evnt) { VegetationSpawn_DrawInstances( context, resources, b, evnt._indexCount, evnt._firstIndex, evnt._firstVertex); }); }
PreparedRTShadowFrustum PrepareRTShadows( IThreadContext& context, Metal::DeviceContext& metalContext, LightingParserContext& parserContext, PreparedScene& preparedScene, const ShadowProjectionDesc& frustum, unsigned shadowFrustumIndex) { SceneParseSettings sceneParseSettings( SceneParseSettings::BatchFilter::RayTracedShadows, ~SceneParseSettings::Toggles::BitField(0), shadowFrustumIndex); if (!parserContext.GetSceneParser()->HasContent(sceneParseSettings)) return PreparedRTShadowFrustum(); Metal::GPUProfiler::DebugAnnotation anno(metalContext, L"Prepare-RTShadows"); auto& box = Techniques::FindCachedBox2<RTShadowsBox>(256, 256, 1024*1024, 32, 64*1024); auto oldSO = Metal::GeometryShader::GetDefaultStreamOutputInitializers(); static const Metal::InputElementDesc soVertex[] = { Metal::InputElementDesc("A", 0, Metal::NativeFormat::R32G32B32A32_FLOAT), Metal::InputElementDesc("B", 0, Metal::NativeFormat::R32G32_FLOAT), Metal::InputElementDesc("C", 0, Metal::NativeFormat::R32G32B32A32_FLOAT), Metal::InputElementDesc("D", 0, Metal::NativeFormat::R32G32B32_FLOAT) }; static const Metal::InputElementDesc il[] = { Metal::InputElementDesc("A", 0, Metal::NativeFormat::R32G32B32A32_FLOAT), Metal::InputElementDesc("B", 0, Metal::NativeFormat::R32G32_FLOAT), Metal::InputElementDesc("C", 0, Metal::NativeFormat::R32G32B32A32_FLOAT), Metal::InputElementDesc("D", 0, Metal::NativeFormat::R32G32B32_FLOAT) }; metalContext.UnbindPS<Metal::ShaderResourceView>(5, 3); const unsigned bufferCount = 1; unsigned strides[] = { 52 }; unsigned offsets[] = { 0 }; Metal::GeometryShader::SetDefaultStreamOutputInitializers( Metal::GeometryShader::StreamOutputInitializers(soVertex, dimof(soVertex), strides, 1)); static_assert(bufferCount == dimof(strides), "Stream output buffer count mismatch"); static_assert(bufferCount == dimof(offsets), "Stream output buffer count mismatch"); metalContext.BindSO(MakeResourceList(box._triangleBufferVB)); // set up the render state for writing into the grid buffer SavedTargets savedTargets(metalContext); metalContext.Bind(box._gridBufferViewport); metalContext.Unbind<Metal::RenderTargetView>(); metalContext.Bind(Techniques::CommonResources()._blendOpaque); metalContext.Bind(Techniques::CommonResources()._defaultRasterizer); // for newer video cards, we need "conservative raster" enabled PreparedRTShadowFrustum preparedResult; preparedResult.InitialiseConstants(&metalContext, frustum._projections); using TC = Techniques::TechniqueContext; parserContext.SetGlobalCB(metalContext, TC::CB_ShadowProjection, &preparedResult._arbitraryCBSource, sizeof(preparedResult._arbitraryCBSource)); parserContext.SetGlobalCB(metalContext, TC::CB_OrthoShadowProjection, &preparedResult._orthoCBSource, sizeof(preparedResult._orthoCBSource)); parserContext.GetTechniqueContext()._runtimeState.SetParameter( StringShadowCascadeMode, (preparedResult._mode == ShadowProjectionDesc::Projections::Mode::Ortho)?2:1); // Now, we need to transform the object's triangle buffer into shadow // projection space during this step (also applying skinning, wind bending, and // any other animation effects. // // Each object that will be used with projected shadows must have a buffer // containing the triangle information. // // We can deal with this in a number of ways: // 1. rtwritetiles shader writes triangles out in a stream-output step // 2. transform triangles first, then pass that information through the rtwritetiles shader // 3. transform triangles completely separately from the rtwritetiles step // // Method 1 would avoid extra transformations of the input data, and actually // simplifies some of the shader work. We don't need any special input buffers // or extra input data. The shaders just take generic model information, and build // everything they need, as they need it. // // We can also choose to reject backfacing triangles at this point, as well as // removing triangles that are culled from the frustum. // Float4x4 savedWorldToProjection = parserContext.GetProjectionDesc()._worldToProjection; parserContext.GetProjectionDesc()._worldToProjection = frustum._worldToClip; auto cleanup = MakeAutoCleanup( [&parserContext, &savedWorldToProjection]() { parserContext.GetProjectionDesc()._worldToProjection = savedWorldToProjection; parserContext.GetTechniqueContext()._runtimeState.SetParameter(StringShadowCascadeMode, 0); }); CATCH_ASSETS_BEGIN parserContext.GetSceneParser()->ExecuteScene( context, parserContext, sceneParseSettings, preparedScene, TechniqueIndex_RTShadowGen); CATCH_ASSETS_END(parserContext) metalContext.UnbindSO(); Metal::GeometryShader::SetDefaultStreamOutputInitializers(oldSO); // We have the list of triangles. Let's render then into the final // grid buffer viewport. This should create a list of triangles for // each cell in the grid. The goal is to reduce the number of triangles // that the ray tracing shader needs to look at. // // We could attempt to do this in the same step above. But that creates // some problems with frustum cull and back face culling. This order // allows us reduce the total triangle count before we start assigning // primitive ids. // // todo -- also calculate min/max for each grid during this step CATCH_ASSETS_BEGIN auto& shader = ::Assets::GetAssetDep<Metal::ShaderProgram>( "game/xleres/shadowgen/rtwritetiles.sh:vs_passthrough:vs_*", "game/xleres/shadowgen/consraster.sh:gs_conservativeRasterization:gs_*", "game/xleres/shadowgen/rtwritetiles.sh:ps_main:ps_*", "OUTPUT_PRIM_ID=1;INPUT_RAYTEST_TRIS=1"); metalContext.Bind(shader); Metal::BoundInputLayout inputLayout(Metal::InputLayout(il, dimof(il)), shader); metalContext.Bind(inputLayout); // no shader constants/resources required unsigned clearValues[] = { 0, 0, 0, 0 }; metalContext.Clear(box._gridBufferUAV, clearValues); metalContext.Bind(Techniques::CommonResources()._blendOpaque); metalContext.Bind(Techniques::CommonResources()._dssDisable); metalContext.Bind(Techniques::CommonResources()._cullDisable); metalContext.Bind(Metal::Topology::PointList); metalContext.Bind(MakeResourceList(box._triangleBufferVB), strides[0], offsets[0]); metalContext.Bind( MakeResourceList(box._dummyRTV), nullptr, MakeResourceList(box._gridBufferUAV, box._listsBufferUAV)); metalContext.DrawAuto(); CATCH_ASSETS_END(parserContext) metalContext.Bind(Metal::Topology::TriangleList); savedTargets.ResetToOldTargets(metalContext); preparedResult._listHeadSRV = box._gridBufferSRV; preparedResult._linkedListsSRV = box._listsBufferSRV; preparedResult._trianglesSRV = box._triangleBufferSRV; return std::move(preparedResult); }