Exemplo n.º 1
0
 static void AmbientOcclusion_DrawDebugging(
     Metal::DeviceContext& context, AmbientOcclusionResources& resources)
 {
     SetupVertexGeneratorShader(context);
     context.BindPS(MakeResourceList(resources._aoSRV));
     context.Bind(::Assets::GetAssetDep<Metal::ShaderProgram>(
         "game/xleres/basic2D.vsh:fullscreen:vs_*", "game/xleres/postprocess/debugging.psh:AODebugging:ps_*"));
     context.Draw(4);
 }
Exemplo n.º 2
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.º 3
0
    void LightingParser_ResolveGBuffer(
        Metal::DeviceContext& context, LightingParserContext& parserContext,
        MainTargetsBox& mainTargets, LightingResolveTextureBox& lightingResTargets)
    {
        Metal::GPUProfiler::DebugAnnotation anno(context, L"ResolveGBuffer");

        const bool doSampleFrequencyOptimisation = Tweakable("SampleFrequencyOptimisation", true);

        LightingResolveContext lightingResolveContext(mainTargets);
        const unsigned samplingCount = lightingResolveContext.GetSamplingCount();
        const bool useMsaaSamplers = lightingResolveContext.UseMsaaSamplers();

        auto& resolveRes = Techniques::FindCachedBoxDep2<LightingResolveResources>(samplingCount);

            //
            //    Our inputs is the prepared gbuffer 
            //        -- we resolve the lighting and write out a "lighting resolve texture"
            //

        if (doSampleFrequencyOptimisation && samplingCount>1) {
            context.Bind(resolveRes._alwaysWriteToStencil, 0xff);

                // todo --  instead of clearing the stencil every time, how 
                //          about doing a progressive walk through all of the bits!
            context.ClearStencil(mainTargets._secondaryDepthBuffer, 0);
            context.Bind(ResourceList<Metal::RenderTargetView, 0>(), &mainTargets._secondaryDepthBuffer);
            context.BindPS(MakeResourceList(mainTargets._msaaDepthBufferSRV, mainTargets._gbufferRTVsSRV[1]));
            SetupVertexGeneratorShader(context);
            CATCH_ASSETS_BEGIN
                context.Bind(*resolveRes._perSampleMask);
                context.Draw(4);
            CATCH_ASSETS_END(parserContext)
        }
Exemplo n.º 4
0
    static void DrawObject(
        Metal::DeviceContext& devContext,
        ParsingContext& parserContext,
        const VisGeoBox& visBox,
        const ResolvedShader& shader, const RetainedEntity& obj)
    {
        if (!obj._properties.GetParameter(Parameters::Visible, true) || !GetShowMarker(obj)) return;

        const auto& cbLayout = ::Assets::GetAssetDep<Techniques::PredefinedCBLayout>(
            "game/xleres/BasicMaterialConstants.txt");

        shader.Apply(devContext, parserContext,
            {
                MakeLocalTransformPacket(
                    GetTransform(obj),
                    ExtractTranslation(parserContext.GetProjectionDesc()._cameraToWorld)),
                cbLayout.BuildCBDataAsPkt(ParameterBox())
            });
        
        devContext.Bind(MakeResourceList(visBox._cubeVB), visBox._cubeVBStride, 0);
        devContext.Draw(visBox._cubeVBCount);
    }
Exemplo n.º 5
0
    static void DrawTriMeshMarker(
        Metal::DeviceContext& devContext,
        ParsingContext& parserContext,
        const VisGeoBox& visBox,
        const ResolvedShader& shader, const RetainedEntity& obj,
        EntityInterface::RetainedEntities& objs)
    {
        static auto IndexListHash = ParameterBox::MakeParameterNameHash("IndexList");

        if (!obj._properties.GetParameter(Parameters::Visible, true) || !GetShowMarker(obj)) return;

        // we need an index list with at least 3 indices (to make at least one triangle)
        auto indexListType = obj._properties.GetParameterType(IndexListHash);
        if (indexListType._type == ImpliedTyping::TypeCat::Void || indexListType._arrayCount < 3)
            return;

        auto ibData = std::make_unique<unsigned[]>(indexListType._arrayCount);
        bool success = obj._properties.GetParameter(
            IndexListHash, ibData.get(), 
            ImpliedTyping::TypeDesc(ImpliedTyping::TypeCat::UInt32, indexListType._arrayCount));
        if (!success) return;

        const auto& chld = obj._children;
        if (!chld.size()) return;

        auto vbData = std::make_unique<Float3[]>(chld.size());
        for (size_t c=0; c<chld.size(); ++c) {
            const auto* e = objs.GetEntity(obj._doc, chld[c]);
            if (e) {
                vbData[c] = ExtractTranslation(GetTransform(*e));
            } else {
                vbData[c] = Zero<Float3>();
            }
        }
        
        const auto& cbLayout = ::Assets::GetAssetDep<Techniques::PredefinedCBLayout>(
            "game/xleres/BasicMaterialConstants.txt");

        shader.Apply(devContext, parserContext,
            {
                MakeLocalTransformPacket(
                    GetTransform(obj),
                    ExtractTranslation(parserContext.GetProjectionDesc()._cameraToWorld)),
                cbLayout.BuildCBDataAsPkt(ParameterBox())
            });

        devContext.Bind(Techniques::CommonResources()._blendAdditive);
        devContext.Bind(Techniques::CommonResources()._dssReadOnly);
        devContext.Bind(Techniques::CommonResources()._cullDisable);
        
        Metal::VertexBuffer vb(vbData.get(), sizeof(Float3)*chld.size());
        Metal::IndexBuffer ib(ibData.get(), sizeof(unsigned)*indexListType._arrayCount);

        devContext.Bind(MakeResourceList(vb), sizeof(Float3), 0);
        devContext.Bind(ib, Metal::NativeFormat::R32_UINT);
        devContext.Bind(Metal::Topology::TriangleList);
        devContext.DrawIndexed(indexListType._arrayCount);
    }
Exemplo n.º 6
0
    bool VegetationSpawn_DrawInstances(
        Metal::DeviceContext& context,
        VegetationSpawnResources& res,
        unsigned instanceId, unsigned indexCount, unsigned startIndexLocation, unsigned baseVertexLocation)
    {
            //  We must draw the currently queued geometry using instance information 
            //  we calculated in the prepare phase.
            //  We have to write the following structure into the indirect args buffer:
            //      struct DrawIndexedInstancedIndirectArgs {
            //          UINT IndexCountPerInstance; 
            //          UINT InstanceCount;
            //          UINT StartIndexLocation;
            //          INT BaseVertexLocation;
            //          UINT StartInstanceLocation;
            //      }
        if (!res._isPrepared || instanceId > res._instanceBufferSRVs.size())
            return false;

            //
            //      We don't have a good way to copy the stream output counts data from the SO target into the 
            //      args buffer. We can do that easily with an AppendStructuredBuffer, but not with an 
            //      SO target.
            //
            //      we have two options:
            //          * Use D3D11.1, and write to an UnorderedAccessView from the geometry shader
            //          * Use a compute shader for the prepare step
            //
            //      Using a compute shader would be more powerful. But we'd have to adjust the mesh rendering
            //      to support Dispatch()ing a compute shader instead of Draw() a shader program
            //
            //      Right now, we can just use the "D3D11_QUERY_SO_STATISTICS" query. But this causes a CPU/GPU
            //      sync! So it needs to be replaced.
            //
            //      Even with D3D11.1, there is a problem. We can't use interlocked instructions from the GS,
            //      so we can't maintain a instance count (or even use AppendStructuredBuffer). Also, the compute
            //      shader only approach won't work for tessellated terrain, because the LOD work for terrain would 
            //      be too complex to re-implement in the compute shader.
            //
            //      To get XLE terrain working; we could use a terrain geometry shader write out the raw triangles
            //      from the tessellation processing -- (which could then be used for both drawing an calculating
            //      instances)... But they we run into the same problem. How do we know the number of primitives
            //      written out by the geometry shader?
            //
            //      Even worse, it might be that the geometry shader won't always write to the start of the output
            //      buffer... With the hit detection code, it seems like stream output will sometimes start at
            //      a random offset in the stream output targets.
            //
            //      So how do we get the instance count? We could use another compute shader to count the number
            //      of valid instances that ended up in the buffer. That would mean clearing the buffer first,
            //      and then looking for instances that are still clear. We could use the same process to 
            //      separate the instances into different bins (for different geometry objects).
            //
            //          -- so we write many instances from the geometry shader first, of the form:
            //              x, y, z, rotation, type (....)
            //
            //      Then we use a compute shader to separate those instances into AppendStructuredBuffer 
            //      so that when we finally drop the objects, we have separate input vertex buffers for each
            //      type of geometry input.
            //
            //      In this way, we get the final result we need (with the number of instances correct). But
            //      it requires an extra pass with the compute shader -- that might add a little overhead.
            //      It seems to be the best way, however, because we keep the flexibility of using a geometry
            //      shader -- and this can be used as part of a shader pipeline with many different vertex
            //      shaders and input geometry types.
            //

        enum PrimitiveCountMethod { FromQuery, FromUAV } primitiveCountMethod = FromUAV;

        unsigned instanceCount = 0;
        if (primitiveCountMethod == FromQuery)
            instanceCount = GetSOPrimitives(&context, res._streamOutputCountsQuery.get());
        res._indirectDrawBuffer.WriteParams(context, indexCount, startIndexLocation, baseVertexLocation, instanceCount);

            // note --  we may be able to use the query to skip the compute shader step
            //          in cases where there is zero vegetation generated. Possibly a GPU
            //          predicate can help avoid a CPU sync when doing that.

        if (primitiveCountMethod == FromUAV)
            res._indirectDrawBuffer.CopyInstanceCount(context, res._instanceBufferUAVs[instanceId]);

            // bind the instancing buffer as an input vertex buffer
            //  This "instancing buffer" is the output from our separation compute shader
        // unsigned stride = 4*4, offset = 0;
        // auto* buffer = res._instanceBuffers[instanceId].get();
        // const unsigned slotForVertexInput = 3;
        // context->GetUnderlying()->IASetVertexBuffers(slotForVertexInput, 1, &buffer, &stride, &offset);
        context.BindVS(MakeResourceList(15, res._instanceBufferSRVs[instanceId]));

            // finally -- draw
        res._indirectDrawBuffer.Draw(context);

        return true;
    }
Exemplo n.º 7
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);
    }