Exemplo n.º 1
0
    unsigned LightingParser_BindLightResolveResources( 
        Metal::DeviceContext& context,
        LightingParserContext& parserContext)
    {
            // bind resources and constants that are required for lighting resolve operations
            // these are needed in both deferred and forward shading modes... But they are
            // bound at different times in different modes

        CATCH_ASSETS_BEGIN
            unsigned skyTextureProjection = SkyTextureParts(parserContext.GetSceneParser()->GetGlobalLightingDesc()).BindPS(context, 11);

            context.BindPS(MakeResourceList(10, ::Assets::GetAssetDep<RenderCore::Assets::DeferredShaderResource>("game/xleres/DefaultResources/balanced_noise.dds:LT").GetShaderResource()));
            context.BindPS(MakeResourceList(16, ::Assets::GetAssetDep<RenderCore::Assets::DeferredShaderResource>("game/xleres/DefaultResources/GGXTable.dds:LT").GetShaderResource()));

            context.BindPS(MakeResourceList(9, Metal::ConstantBuffer(&GlobalMaterialOverride, sizeof(GlobalMaterialOverride))));

            return skyTextureProjection;
        CATCH_ASSETS_END(parserContext)

        return 0;
    }
Exemplo n.º 2
0
    void VegetationSpawn_Prepare(
        Metal::DeviceContext* context, LightingParserContext& parserContext,
        const VegetationSpawnConfig& cfg, VegetationSpawnResources& res)
    {
            //  Prepare the scene for vegetation spawn
            //  This means binding our output buffers to the stream output slots,
            //  and then rendering the terrain with a special technique.
            //  We can use flags to force the scene parser to render only the terrain
            //
            //  If we use "GeometryShader::SetDefaultStreamOutputInitializers", then future
            //  geometry shaders will be created as stream-output shaders.

        using namespace RenderCore;
        auto oldSO = Metal::GeometryShader::GetDefaultStreamOutputInitializers();
        ID3D::Query* begunQuery = nullptr;

        auto oldCamera = parserContext.GetProjectionDesc();

        CATCH_ASSETS_BEGIN
            auto& perlinNoiseRes = Techniques::FindCachedBox2<SceneEngine::PerlinNoiseResources>();
            context->BindGS(MakeResourceList(12, perlinNoiseRes._gradShaderResource, perlinNoiseRes._permShaderResource));
            context->BindGS(MakeResourceList(Metal::SamplerState()));

                //  we have to clear vertex input "3", because this is the instancing input slot -- and 
                //  we're going to be writing to buffers that will be used for instancing.
            // ID3D::Buffer* nullBuffer = nullptr; unsigned zero = 0;
            // context->GetUnderlying()->IASetVertexBuffers(3, 1, &nullBuffer, &zero, &zero);
            context->Unbind<Metal::VertexBuffer>();
            context->UnbindVS<Metal::ShaderResourceView>(15, 1);

            float maxDrawDistance = 0.f;
            for (const auto& m:cfg._materials)
                for (const auto& b:m._buckets)
                    maxDrawDistance = std::max(b._maxDrawDistance, maxDrawDistance);

            class InstanceSpawnConstants
            {
            public:
                Float4x4    _worldToCullFrustum;
                float       _gridSpacing, _baseDrawDistanceSq, _jitterAmount;
                unsigned    _dummy;
                Float4      _materialParams[8];
                Float4      _suppressionNoiseParams[8];
            } instanceSpawnConstants = {
                parserContext.GetProjectionDesc()._worldToProjection,
                cfg._baseGridSpacing, maxDrawDistance*maxDrawDistance, cfg._jitterAmount, 0,
                {   Zero<Float4>(), Zero<Float4>(), Zero<Float4>(), Zero<Float4>(), 
                    Zero<Float4>(), Zero<Float4>(), Zero<Float4>(), Zero<Float4>() },
                {   Zero<Float4>(), Zero<Float4>(), Zero<Float4>(), Zero<Float4>(), 
                    Zero<Float4>(), Zero<Float4>(), Zero<Float4>(), Zero<Float4>() }
            };

            for (unsigned mi=0; mi<std::min(cfg._materials.size(), dimof(instanceSpawnConstants._materialParams)); ++mi) {
                instanceSpawnConstants._materialParams[mi][0] = cfg._materials[mi]._suppressionThreshold;

                instanceSpawnConstants._suppressionNoiseParams[mi][0] = cfg._materials[mi]._suppressionNoise;
                instanceSpawnConstants._suppressionNoiseParams[mi][1] = cfg._materials[mi]._suppressionGain;
                instanceSpawnConstants._suppressionNoiseParams[mi][2] = cfg._materials[mi]._suppressionLacunarity;
            }

            context->BindGS(MakeResourceList(5, Metal::ConstantBuffer(&instanceSpawnConstants, sizeof(InstanceSpawnConstants))));

            const bool needQuery = false;
            if (constant_expression<needQuery>::result()) {
                begunQuery = res._streamOutputCountsQuery.get();
                context->GetUnderlying()->Begin(begunQuery);
            }

            static const Metal::InputElementDesc eles[] = {

                    //  Our instance format is very simple. It's just a position and 
                    //  rotation value (in 32 bit floats)
                    //  I'm not sure if the hardware will support 16 bit floats in a
                    //  stream output buffer (though maybe we could use fixed point
                    //  16 bit integers?)
                    //  We write a "type" value to a second buffer. Let's keep that 
                    //  buffer as small as possible, because we have to clear it 
                    //  before hand

                Metal::InputElementDesc("INSTANCEPOS", 0, Metal::NativeFormat::R32G32B32A32_FLOAT),
                    // vertex in slot 1 must have a vertex stride that is a multiple of 4
                Metal::InputElementDesc("INSTANCEPARAM", 0, Metal::NativeFormat::R32_UINT, 1)
            };

                //  How do we clear an SO buffer? We can't make it an unorderedaccess view or render target.
                //  The only obvious way is to use CopyResource, and copy from a prepared "cleared" buffer
            Metal::Copy(*context, res._streamOutputResources[1]->GetUnderlying(), res._clearedTypesResource->GetUnderlying());

            unsigned strides[2] = { Stream0VertexSize, Stream1VertexSize };
            Metal::GeometryShader::SetDefaultStreamOutputInitializers(
                Metal::GeometryShader::StreamOutputInitializers(eles, dimof(eles), strides, 2));

            SceneParseSettings parseSettings(
                SceneParseSettings::BatchFilter::General,
                SceneParseSettings::Toggles::Terrain);

                // Adjust the far clip so that it's very close...
                // We might want to widen the field of view slightly
                // by moving the camera back a bit. This could help make
                // sure that objects near the camera and on the edge of the screen
                // get included
            auto newProjDesc = AdjustProjDesc(oldCamera, maxDrawDistance);

                //  We have to call "SetGlobalTransform" to force the camera changes to have effect.
                //  Ideally there would be a cleaner way to automatically update the constants
                //  when the bound camera changes...
            LightingParser_SetGlobalTransform(*context, parserContext, newProjDesc);

            context->BindSO(MakeResourceList(res._streamOutputBuffers[0], res._streamOutputBuffers[1]));

            parserContext.GetSceneParser()->ExecuteScene(context, parserContext, parseSettings, 5);

            context->UnbindSO();

                //  After the scene execute, we need to use a compute shader to separate the 
                //  stream output data into it's bins.
            static const unsigned MaxOutputBinCount = 8;
            ID3D::UnorderedAccessView* outputBins[MaxOutputBinCount];
            unsigned initialCounts[MaxOutputBinCount];
            std::fill(outputBins, &outputBins[dimof(outputBins)], nullptr);
            std::fill(initialCounts, &initialCounts[dimof(initialCounts)], 0);

            auto outputBinCount = std::min((unsigned)cfg._objectTypes.size(), (unsigned)res._instanceBufferUAVs.size());
            for (unsigned c=0; c<outputBinCount; ++c) {
                unsigned clearValues[] = { 0, 0, 0, 0 };
                context->Clear(res._instanceBufferUAVs[c], clearValues);
                outputBins[c] = res._instanceBufferUAVs[c].GetUnderlying();
            }

            context->BindCS(MakeResourceList(res._streamOutputSRV[0], res._streamOutputSRV[1]));
            context->GetUnderlying()->CSSetUnorderedAccessViews(0, outputBinCount, outputBins, initialCounts);

            class InstanceSeparateConstants
            {
            public:
                UInt4 _binThresholds[16];
                Float4 _drawDistanceSq[16];
            } instanceSeparateConstants;
            XlZeroMemory(instanceSeparateConstants);

            StringMeld<1024> shaderParams;

            unsigned premapBinCount = 0;
            for (unsigned mi=0; mi<cfg._materials.size(); ++mi) {
                const auto& m = cfg._materials[mi];
                float combinedWeight = 0.f; // m._noSpawnWeight;
                for (const auto& b:m._buckets) combinedWeight += b._frequencyWeight;

                unsigned weightIterator = 0;
                for (unsigned c=0; c<std::min(dimof(instanceSeparateConstants._binThresholds), m._buckets.size()); ++c) {
                    weightIterator += unsigned(4095.f * m._buckets[c]._frequencyWeight / combinedWeight);

                    instanceSeparateConstants._binThresholds[premapBinCount][0] = (mi<<12) | weightIterator;
                    instanceSeparateConstants._drawDistanceSq[premapBinCount][0] 
                        = m._buckets[c]._maxDrawDistance * m._buckets[c]._maxDrawDistance;

                    shaderParams << "OUTPUT_BUFFER_MAP" << premapBinCount << "=" << m._buckets[c]._objectType << ";";
                    ++premapBinCount;
                }
            }
            for (unsigned c=premapBinCount; c<dimof(instanceSeparateConstants._binThresholds); ++c)
                shaderParams << "OUTPUT_BUFFER_MAP" << c << "=0;";

            shaderParams << "INSTANCE_BIN_COUNT=" << premapBinCount;

            context->BindCS(MakeResourceList(
                parserContext.GetGlobalTransformCB(),
                Metal::ConstantBuffer(&instanceSeparateConstants, sizeof(instanceSeparateConstants))));

            context->Bind(::Assets::GetAssetDep<Metal::ComputeShader>(
                "game/xleres/Vegetation/InstanceSpawnSeparate.csh:main:cs_*", 
                shaderParams.get()));
            context->Dispatch(StreamOutputMaxCount / 256);

                // unbind all of the UAVs again
            context->UnbindCS<Metal::UnorderedAccessView>(0, outputBinCount);
            context->UnbindCS<Metal::ShaderResourceView>(0, 2);

            res._isPrepared = true;

        CATCH_ASSETS_END(parserContext)

        if (begunQuery) {
            context->GetUnderlying()->End(begunQuery);
        }

            // (reset the camera transform if it's changed)
        LightingParser_SetGlobalTransform(*context, parserContext, oldCamera);

        context->UnbindSO();
        Metal::GeometryShader::SetDefaultStreamOutputInitializers(oldSO);
        // oldTargets.ResetToOldTargets(context);
    }
Exemplo n.º 3
0
    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);
    }
Exemplo n.º 4
0
    void TerrainManager::Render(Metal::DeviceContext* context, LightingParserContext& parserContext, unsigned techniqueIndex)
    {
        assert(_pimpl);
        auto* renderer = _pimpl->_renderer.get();
        if (!renderer) return;

            //  we need to enable the rendering state once, for all cells. The state should be
            //  more or less the same for every cell, so we don't need to do it every time
        TerrainRenderingContext state(
            renderer->GetCoverageIds(), 
            renderer->GetCoverageFmts(), renderer->GetCoverageLayersCount(), 
            _pimpl->_cfg.EncodedGradientFlags());
        state._queuedNodes.erase(state._queuedNodes.begin(), state._queuedNodes.end());
        state._queuedNodes.reserve(2048);
        state._currentViewport = Metal::ViewportDesc(*context);
        _pimpl->CullNodes(context, parserContext, state);

        renderer->CompletePendingUploads();
        renderer->QueueUploads(state);

        if (!_pimpl->_textures || _pimpl->_textures->GetDependencyValidation()->GetValidationIndex() > 0) {
            _pimpl->_textures.reset();
            _pimpl->_textures = std::make_unique<TerrainMaterialTextures>(
                *context, _pimpl->_matCfg, _pimpl->_cfg.EncodedGradientFlags());
        }

        context->BindPS(MakeResourceList(8, 
            _pimpl->_textures->_srv[TerrainMaterialTextures::Diffuse], 
            _pimpl->_textures->_srv[TerrainMaterialTextures::Normal], 
            _pimpl->_textures->_srv[TerrainMaterialTextures::Specularity]));

        auto mode = 
            (techniqueIndex==5)
            ? TerrainRenderingContext::Mode_VegetationPrepare
            : TerrainRenderingContext::Mode_Normal;

        Float3 sunDirection(0.f, 0.f, 1.f);
        if (parserContext.GetSceneParser() && parserContext.GetSceneParser()->GetLightCount() > 0) {
            sunDirection = parserContext.GetSceneParser()->GetLightDesc(0)._negativeLightDirection;
        }

            // We want to project the sun direction onto the plane for the precalculated sun movement.
            // Then find the appropriate angle for on that plane.
        float sunDirectionAngle;
        {
            auto trans = Identity<Float4x4>();
            Combine_InPlace(trans, RotationZ(-_pimpl->_cfg.SunPathAngle()));
            auto transDirection = TransformDirectionVector(trans, sunDirection);
            sunDirectionAngle = XlATan2(transDirection[0], transDirection[2]);
        }

        auto shadowSoftness = Tweakable("ShadowSoftness", 15.f);
        const float expansionConstant = 1.5f;
        float terrainLightingConstants[] = { sunDirectionAngle / float(.5f * expansionConstant * M_PI), shadowSoftness, 0.f, 0.f };
        Metal::ConstantBuffer lightingConstantsBuffer(terrainLightingConstants, sizeof(terrainLightingConstants));
        context->BindPS(MakeResourceList(5, _pimpl->_textures->_texturingConstants, lightingConstantsBuffer, _pimpl->_textures->_procTexContsBuffer));
        if (mode == TerrainRenderingContext::Mode_VegetationPrepare) {
                // this cb required in the geometry shader for vegetation prepare mode!
            context->BindGS(MakeResourceList(6, lightingConstantsBuffer));  
        }

        state.EnterState(context, parserContext, *_pimpl->_textures, renderer->GetHeightsElementSize(), mode);
        renderer->Render(context, parserContext, state);
        state.ExitState(context, parserContext);

        // if (_pimpl->_coverageInterfaces.size() > 0)
        //     parserContext._pendingOverlays.push_back(
        //         std::bind(
        //             &GenericUberSurfaceInterface::RenderDebugging, _pimpl->_coverageInterfaces[0]._interface.get(), 
        //             std::placeholders::_1, std::placeholders::_2));
    }