private void EmitStageInterface <T>( PassEmitContext emitPass) where T : D3D11Stage, new() { var stage = new T() { EmitPass = emitPass }; stage.EmitInterface(); }
private void EmitPipeline( IEmitModule emitModule, MidPipelineDecl midPipeline) { var range = midPipeline.Range; // Create the class to represent the pipeline EmitClassFlags ifaceFlags = EmitClassFlags.None; EmitClassFlags implFlags = EmitClassFlags.Hidden | EmitClassFlags.Implementation; if (!midPipeline.IsPrimary) { ifaceFlags |= EmitClassFlags.Mixin; // The "impl" class is treated as always primary } // this is a terrible horrible hack to detect a stdlib clas // (which means we *don't* want it in the generated header... var className = midPipeline.Name.ToString(); if (className.StartsWith("D3D11")) { ifaceFlags |= EmitClassFlags.Internal; implFlags |= EmitClassFlags.Internal; className = string.Format("spark::d3d11::{0}", className); } var baseFacets = (from f in midPipeline.Facets where f != midPipeline.DirectFacet select f).ToArray(); var primaryBaseFacet = (from f in baseFacets where f.OriginalShaderClass.Decl.IsPrimary select f).FirstOrDefault(); ShaderClassInfo primaryBaseInfo = null; IEmitClass primaryBaseClass = null; if (primaryBaseFacet != null) { primaryBaseInfo = _mapShaderClassToInfo[primaryBaseFacet.OriginalShaderClass.Decl]; primaryBaseClass = primaryBaseInfo.InterfaceClass; } var pipelineEnv = new EmitEnv(_moduleEnv); IEmitClass ifaceClass = emitModule.CreateClass( className, midPipeline.IsPrimary ? primaryBaseClass : null, ifaceFlags); var info = new ShaderClassInfo { MidClassDecl = midPipeline, InheritedFacets = new ShaderFacetInfo[midPipeline.Facets.Count() - 1], }; info.InterfaceClass = ifaceClass; _mapShaderClassToInfo[midPipeline] = info; // Before we create fields declared in Spark, we // first create the "hidden" fields that represent // an instance at run-time: if (primaryBaseInfo == null && midPipeline.IsPrimary) { // We need two fields here for any root-of-the-hierarchy // class (which should actually be declared off in a header somewhere). // The first is a "class info" pointer: ifaceClass.AddPrivateField( Target.GetOpaqueType("void*"), "__classInfo"); // The second is the reference-count field: ifaceClass.AddPrivateField( Target.GetBuiltinType("UINT"), "__referenceCount"); } // Create fields to represent the various attributes var constantElement = GetElement(midPipeline, "Constant"); var uniformElement = GetElement(midPipeline, "Uniform"); // Direct facet: var directFacetDecl = midPipeline.DirectFacet; var directFacetInfo = CreateDirectFacet( directFacetDecl, constantElement, uniformElement, ifaceClass, pipelineEnv); info.DirectFacet = directFacetInfo; ifaceClass.WrapperWriteLineProtected( "{0}* _StaticCastImpl( void* ) {{ return this; }}", className); // Base-class facet, if any if (primaryBaseInfo != null) { if (midPipeline.IsPrimary) { AddPrimaryBaseFacet( info, primaryBaseInfo, (b, shaderObj) => shaderObj); } else { var fieldName = "_Base_" + primaryBaseInfo.MidClassDecl.Name.ToString(); var fieldType = primaryBaseInfo.InterfaceClass.Pointer(); var facetField = ifaceClass.AddPrivateField( fieldType, fieldName); info.DirectFacet.Mixins.Add( new ShaderFacetMixinInfo( primaryBaseInfo.MidClassDecl, facetField)); AddPrimaryBaseFacet( info, primaryBaseInfo, (b, shaderObj) => b.GetArrow(shaderObj, facetField)); foreach (var bf in primaryBaseInfo.AllFacets) { var baseFacet = bf; // avoid capture AddForwardingAccessors( info.InterfaceClass, baseFacet); var baseClass = _mapShaderClassToInfo[baseFacet.OriginalClass]; ifaceClass.WrapperWriteLineProtected( "{0}* _StaticCastImpl( {0}* ) {{ return {1}; }}", baseClass.InterfaceClass.GetName(), fieldName); } } } // Inherited facets: foreach (var f in midPipeline.Facets) { // Clearly don't want to embed a facet for ourself... if (f == midPipeline.DirectFacet) { continue; } // Primary classes will already be covered by // the inheritance chain... if (f.OriginalShaderClass.Decl.IsPrimary) { continue; } // We may have inherited an interface // to this facet through our base... if (primaryBaseFacet != null) { if (primaryBaseFacet.OriginalShaderClass.Decl.Facets.Any( (otherF) => otherF.OriginalShaderClass.Decl == f.OriginalShaderClass.Decl)) { continue; } } // Otherwise we need a public field // to expose the facet: var facetClassDecl = f.OriginalShaderClass.Decl; var facetClassInfo = _mapShaderClassToInfo[facetClassDecl]; // \todo: get pointer type... :( var fieldType = facetClassInfo.InterfaceClass.Pointer(); var fieldName = string.Format( "_Mixin_{0}", f.OriginalShaderClass.Decl.Name.ToString()); var field = ifaceClass.AddPrivateField(fieldType, fieldName); info.DirectFacet.Mixins.Add( new ShaderFacetMixinInfo( facetClassDecl, field)); ifaceClass.WrapperWriteLineProtected( "{0} _StaticCastImpl( {0} ) {{ return {1}; }}", fieldType.ToString(), fieldName); AddBaseFacet( info, facetClassInfo.DirectFacet, (b, shaderObj) => b.GetArrow(shaderObj, field)); AddForwardingAccessors( info.InterfaceClass, facetClassInfo.DirectFacet); } /* * * * * foreach (var a in constantElement.Attributes) * { * var attr = a; // Avoid capture. * * if (attr.Exp == null) continue; * * pipelineEnv.Insert(attr, * (b) => EmitExp(attr.Exp, b, pipelineEnv)); * } * foreach (var a in uniformElement.Attributes) * { * var attr = a; // Avoid capture. * * if (attr.Exp != null) continue; * * var attrType = EmitType(attr.Type, pipelineEnv); * var attrName = attr.Name.ToString(); * * var attrField = emitClass.AddPublicField(attrType, attrName); * * pipelineEnv.Insert(attr, * (b) => b.GetArrow( * b.Method.ThisParameter, * attrField)); * } */ var sharedHLSL = new HLSL.SharedContextHLSL(Identifiers, Diagnostics); var emitPass = new PassEmitContext() { EmitContext = this, MidPass = midPipeline, SharedHLSL = sharedHLSL, EmitClass = ifaceClass, ShaderClassEnv = pipelineEnv, ShaderClassInfo = info, }; // Now emit stage-specific code. EmitStageInterface <D3D11VertexShader>(emitPass); EmitStageInterface <D3D11HullShader>(emitPass); EmitStageInterface <D3D11DomainShader>(emitPass); EmitStageInterface <D3D11GeometryShader>(emitPass); EmitStageInterface <D3D11PixelShader>(emitPass); // IA last since it generates the call to Draw*() EmitStageInterface <D3D11InputAssembler>(emitPass); // Do this *after* emitting the per-stage interface, // so that stages of the pipeline can bind attributes too. foreach (var f in info.AllFacets) { foreach (var a in f.Attributes) { if (a == null) { continue; } var attr = a.AttributeDecl; var accessor = a.Accessor; pipelineEnv.Insert( attr, (b) => accessor(b, b.Method.ThisParameter)); } } ifaceClass.WrapperWriteLine(""); ifaceClass.WrapperWriteLine( "// Statically cast shader to base/mixin class"); ifaceClass.WrapperWriteLine( "template<typename TBase>"); ifaceClass.WrapperWriteLine( "TBase* StaticCast() { return _StaticCastImpl(static_cast<TBase*>(nullptr)); }"); ifaceClass.WrapperWriteLine(""); ifaceClass.WrapperWriteLine( "// Spark implementation details follow. Do not depend on these:"); ifaceClass.WrapperWriteLine("//"); // Janky RTTI-like system: use the name of the class ifaceClass.WrapperWriteLine( "static const char* StaticGetShaderClassName() {{ return \"{0}\"; }}", className); ifaceClass.Seal(); if (midPipeline.IsAbstract) { return; } var implBase = ifaceClass; if (!midPipeline.IsPrimary) { implBase = primaryBaseClass; if (implBase == null) { throw new NotImplementedException(); } } var implClass = emitModule.CreateClass( className, implBase, implFlags); ifaceClass.WrapperWriteLine( "static const spark::ShaderClassDesc* GetShaderClassDesc();"); // // The impl class needs a field to hold each of its mixin bases... Dictionary <MidPipelineDecl, Func <IEmitBlock, IEmitVal> > getFacetPointerForBase = new Dictionary <MidPipelineDecl, Func <IEmitBlock, IEmitVal> >(); var facetInfoData = new List <IEmitVal>(); var facetInfoCount = 0; foreach (var f in midPipeline.Facets) { // Clearly don't want to embed a facet for ourself... var facetClassDecl = f.OriginalShaderClass.Decl; if (facetClassDecl.IsPrimary) { // Primary classes will already be covered by // the inheritance chain... getFacetPointerForBase[facetClassDecl] = (b) => b.Method.ThisParameter; facetInfoData.Add(emitModule.LiteralString(_mapShaderClassToInfo[facetClassDecl].InterfaceClass.GetName())); facetInfoData.Add(Target.LiteralU32(0)); facetInfoCount++; } } UInt32 facetOffset = ifaceClass.Size; foreach (var f in midPipeline.Facets) { // Clearly don't want to embed a facet for ourself... var facetClassDecl = f.OriginalShaderClass.Decl; if (!facetClassDecl.IsPrimary) { // Mixin classes will be reflected as fields. var facetClassInfo = _mapShaderClassToInfo[facetClassDecl]; var fieldType = facetClassInfo.InterfaceClass; var fieldName = string.Format( "_MixinImpl_{0}", f.OriginalShaderClass.Decl.Name.ToString()); var field = implClass.AddPrivateField(fieldType, fieldName); getFacetPointerForBase[facetClassDecl] = (b) => b.GetArrow(b.Method.ThisParameter, field).GetAddress(); facetInfoData.Add(emitModule.LiteralString(_mapShaderClassToInfo[facetClassDecl].InterfaceClass.GetName())); facetInfoData.Add(Target.LiteralU32(facetOffset)); facetInfoCount++; facetOffset += fieldType.Size; } } var facetInfoVal = emitModule.EmitGlobalStruct( null, facetInfoData.ToArray()).GetAddress(); // var deviceType = Target.GetOpaqueType("ID3D11Device*"); var contextType = Target.GetOpaqueType("ID3D11DeviceContext*"); // Create constructor var ctor = implClass.CreateCtor(); var ctorDevice = ctor.AddParameter( deviceType, "device"); var facetInitBlock = ctor.EntryBlock.InsertBlock(); var cbInit = ctor.EntryBlock.InsertBlock(); // First things first: wire up all the various facets to // the appropriate pointers: Dictionary <MidPipelineDecl, IEmitVal> initFacetPointers = new Dictionary <MidPipelineDecl, IEmitVal>(); foreach (var p in getFacetPointerForBase) { var facet = p.Key; var accessor = p.Value; var val = accessor(facetInitBlock); initFacetPointers[facet] = val; } foreach (var f in info.AllFacets) { var facetPointer = initFacetPointers[f.OriginalClass]; foreach (var m in f.Mixins) { var mixinPointer = initFacetPointers[m.OriginalClass]; facetInitBlock.SetArrow( facetPointer, m.MixinField, facetInitBlock.CastRawPointer( mixinPointer, m.MixinField.Type)); } } // Create destructor var dtor = implClass.CreateDtor(); var cbFinit = dtor.EntryBlock.InsertBlock(); // Create Submit() method var submit = implClass.CreateMethod( Target.VoidType, "Submit"); var submitDevice = submit.AddParameter( deviceType, "device"); var submitContext = submit.AddParameter( contextType, "context"); var submitEnv = new EmitEnv(pipelineEnv); var cbSubmit = submit.EntryBlock.InsertBlock(); var cbPointerType = Target.GetOpaqueType("ID3D11Buffer*"); var cbField = implClass.AddPrivateField( cbPointerType, "_cb"); emitPass = new PassEmitContext() { EmitContext = this, MidPass = midPipeline, SharedHLSL = sharedHLSL, InitBlock = ctor.EntryBlock, ExecBlock = submit.EntryBlock, DtorBlock = dtor.EntryBlock, EmitClass = implClass, CtorDevice = ctorDevice, SubmitContext = submitContext, CtorThis = ctor.ThisParameter, SubmitThis = submit.ThisParameter, DtorThis = dtor.ThisParameter, CBField = cbField, SubmitEnv = submitEnv, }; // Now emit stage-specific code. var iaStage = new D3D11InputAssembler() { EmitPass = emitPass, Range = range }; var vsStage = new D3D11VertexShader() { EmitPass = emitPass, Range = range }; var hsStage = new D3D11HullShader() { EmitPass = emitPass, Range = range }; var dsStage = new D3D11DomainShader() { EmitPass = emitPass, Range = range }; var gsStage = new D3D11GeometryShader() { EmitPass = emitPass, Range = range }; var psStage = new D3D11PixelShader() { EmitPass = emitPass, Range = range }; vsStage.EmitImplSetup(); iaStage.EmitImplSetup(); // IA after VS for bytecode dependency hsStage.EmitImplSetup(); dsStage.EmitImplSetup(); gsStage.EmitImplSetup(); psStage.EmitImplSetup(); psStage.EmitImplBindOM(); // OM first iaStage.EmitImplBind(); // IA as early as possible vsStage.EmitImplBind(); hsStage.EmitImplBind(); dsStage.EmitImplBind(); gsStage.EmitImplBind(); psStage.EmitImplBind(); iaStage.EmitImplDraw(); // Generate code to fill out CB after all the // stage-specific shader stuff, since these are // what compute the required @Uniform values. var block = cbInit; var cbDescVal = block.Temp( "cbDesc", block.Struct( "D3D11_BUFFER_DESC", block.LiteralU32((UInt32)sharedHLSL.ConstantBufferSize), block.Enum32("D3D11_USAGE", "D3D11_USAGE_DYNAMIC", D3D11Stage.D3D11_USAGE.D3D11_USAGE_DYNAMIC), block.LiteralU32((UInt32)D3D11Stage.D3D11_BIND_FLAG.D3D11_BIND_CONSTANT_BUFFER), block.LiteralU32((UInt32)D3D11Stage.D3D11_CPU_ACCESS_FLAG.D3D11_CPU_ACCESS_WRITE), block.LiteralU32(0), block.LiteralU32(0))); block.SetArrow( ctor.ThisParameter, cbField, cbPointerType.Null()); block.CallCOM( ctorDevice, "ID3D11Device", "CreateBuffer", cbDescVal.GetAddress(), Target.GetBuiltinType("D3D11_SUBRESOURCE_DATA").Pointer().Null(), block.GetArrow(ctor.ThisParameter, cbField).GetAddress()); block = cbSubmit; var mappedType = Target.GetBuiltinType("D3D11_MAPPED_SUBRESOURCE"); var cbMappedVal = block.Local("_cbMapped", mappedType); block.CallCOM( submitContext, "ID3D11DeviceContext", "Map", block.GetArrow(submit.ThisParameter, cbField), block.LiteralU32(0), block.Enum32("D3D11_MAP", "D3D11_MAP_WRITE_DISCARD", D3D11Stage.D3D11_MAP.D3D11_MAP_WRITE_DISCARD), block.LiteralU32(0), cbMappedVal.GetAddress()); IEmitVal mappedData = block.Temp( "cbMappedData", block.CastRawPointer( block.GetBuiltinField(cbMappedVal, "pData", Target.GetOpaqueType("void*")))); foreach (var u in sharedHLSL.Uniforms) { var val = EmitExp(u.Val, block, pipelineEnv); block.StoreRaw( mappedData, (UInt32)u.ByteOffset, val); } block.CallCOM( submitContext, "ID3D11DeviceContext", "Unmap", block.GetArrow(submit.ThisParameter, cbField), block.LiteralU32(0)); cbFinit.CallCOM( cbFinit.GetArrow(dtor.ThisParameter, cbField), "ID3D11Buffer", "Release"); // Now generate calls to bind the depth-stencil and rasterizer states var rsStateAttr = FindAttribute(midPipeline, "Uniform", "RS_State").First(); var rsStateVal = EmitAttributeRef( rsStateAttr, block, pipelineEnv); block.CallCOM( submitContext, "ID3D11DeviceContext", "RSSetState", rsStateVal); var omDepthStencilStateAttr = FindAttribute(midPipeline, "Uniform", "OM_DepthStencilState").First(); var omDepthStencilStateVal = EmitAttributeRef( omDepthStencilStateAttr, block, pipelineEnv); var omStencilRefAttr = FindAttribute(midPipeline, "Uniform", "OM_StencilRef").First(); var omStencilRefVal = EmitAttributeRef( omStencilRefAttr, block, pipelineEnv); block.CallCOM( submitContext, "ID3D11DeviceContext", "OMSetDepthStencilState", omDepthStencilStateVal, omStencilRefVal); implClass.Seal(); // Now we need to constrct the class-info // structure, that will be used as a kind of // virtual function table at runtime. var classInfoVal = emitModule.EmitGlobalStruct( className, new IEmitVal[] { Target.LiteralU32(implClass.Size), Target.LiteralU32((UInt32)facetInfoCount), facetInfoVal, emitModule.GetMethodPointer(ctor), emitModule.GetMethodPointer(dtor), emitModule.GetMethodPointer(submit), }); if (emitModule is Emit.CPlusPlus.EmitModuleCPP) { var moduleCPP = (Emit.CPlusPlus.EmitModuleCPP)emitModule; moduleCPP.SourceSpan.WriteLine( "const spark::ShaderClassDesc* {0}::GetShaderClassDesc() {{ return reinterpret_cast<const spark::ShaderClassDesc*>(&({1})); }}", ifaceClass, classInfoVal); } }