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); }
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; }
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) }
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); }
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); }
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; }
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); }