using System.Numerics; using System.Runtime.InteropServices; using Microsoft.UI.Composition; using Vortice.D3DCompiler; using Vortice.Direct3D; using Vortice.Direct3D11; using Vortice.DXGI; using Vortice.Mathematics; using Vortice.WinUI.Composition; using XFEEDToolBox.Core.Rendering; using XFEEDToolBox.Core.Galaxy; using static Vortice.Direct3D11.D3D11; namespace XFEEDToolBox.Rendering; [StructLayout(LayoutKind.Sequential)] internal readonly record struct GalaxyStar(Vector2 Position,float Radius,float Kind,Vector4 Colour,Vector4 World=default,float OriginalSize=0,bool PowerTerritorySize=false,int OriginalSprite=13,Vector4? OriginalColour=null,bool OriginalBlackHole=false); [StructLayout(LayoutKind.Sequential)] internal readonly record struct GalaxyStellarSprite(Vector2 Position,float Radius,float Kind,Vector4 Colour,Vector4 Appearance); [StructLayout(LayoutKind.Sequential)] internal readonly record struct GalaxyLineVertex(Vector2 Position,float Distance,float Dashed,Vector4 Colour); /// One instanced star draw and one line draw, independent of the number of retained XAML/Composition nodes. internal sealed partial class GalaxyGraphics : IDisposable { private readonly ID3D11Device device; private readonly ID3D11DeviceContext context; private readonly ID3D11Buffer starBuffer,lineBuffer,constants; private readonly ID3D11VertexShader starsVs,linesVs,skyVs,overviewVs; private readonly ID3D11PixelShader starsPs,linesPs,skyPs,overviewPs,inspectSkyPs; private readonly ID3D11InputLayout starsLayout,linesLayout; private readonly ID3D11BlendState additive; private readonly ID3D11SamplerState sampler; private readonly ID3D11RasterizerState rasterizer; private readonly ID3D11DepthStencilState noDepth; private readonly CompositionGraphicsDevice compositionDevice; private readonly ICompositionDrawingSurfaceInterop interop; private readonly BeginDraw beginDraw; private readonly ResizeDraw resizeDraw; private ID3D11Texture2D? pixels,skyTexture; private ID3D11RenderTargetView? target; private ID3D11ShaderResourceView? sky; private ID3D11Texture2D? densityTexture; private ID3D11ShaderResourceView? density; private GalaxyDensityMap? densityMap; private int width,height; private long nextSkyCheck; private readonly GalaxyStellarSprite[] stellarSprites=new GalaxyStellarSprite[StarCapacity]; public CompositionDrawingSurface Surface { get; } public const int StarCapacity=65536,LineVertexCapacity=52000; public long Frames { get; private set; } public int DrawCalls { get; private set; } internal bool UnresolvedStarBackgroundEnabled {get;private set;} public string SkyboxSource { get; private set; }=""; public GalaxyDensityMap? Overview=>densityMap; internal (int Width,int Height,byte[] Pixels) ReadPixels() { if(pixels is null)throw new InvalidOperationException("星图尚未绘制。"); var description=pixels.Description;description.BindFlags=BindFlags.None;description.CPUAccessFlags=CpuAccessFlags.Read; description.Usage=ResourceUsage.Staging;description.MiscFlags=ResourceOptionFlags.None; using var staging=device.CreateTexture2D(description);context.CopyResource(staging,pixels); var mapped=context.Map(staging,0,MapMode.Read); try { var result=new byte[width*height*4]; for(var y=0;y.FromManaged(compositor))) using(var api=unknown.QueryInterface()) {api.CreateGraphicsDevice(device,out var created).CheckError();compositionDevice=created!;} Surface=compositionDevice.CreateDrawingSurface(new Windows.Foundation.Size(1,1), Microsoft.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized,Microsoft.Graphics.DirectX.DirectXAlphaMode.Premultiplied); using(var unknown=new SharpGen.Runtime.ComObject(WinRT.MarshalInspectable.FromManaged(Surface))) interop=unknown.QueryInterface(); beginDraw=Marshal.GetDelegateForFunctionPointer(Marshal.ReadIntPtr(Marshal.ReadIntPtr(interop.NativePointer),3*IntPtr.Size)); resizeDraw=Marshal.GetDelegateForFunctionPointer(Marshal.ReadIntPtr(Marshal.ReadIntPtr(interop.NativePointer),5*IntPtr.Size)); starBuffer=device.CreateBuffer(StarCapacity*48,BindFlags.VertexBuffer,ResourceUsage.Dynamic,CpuAccessFlags.Write); lineBuffer=device.CreateBuffer(LineVertexCapacity*32,BindFlags.VertexBuffer,ResourceUsage.Dynamic,CpuAccessFlags.Write); constants=device.CreateBuffer(80,BindFlags.ConstantBuffer); var shader=Path.Combine(AppContext.BaseDirectory,"Assets","GalaxyMap.hlsl"); InitializeNebulae(shader); InitializeGalacticZones(shader); InitializePowerContours(shader); InitializeOriginalSprites(); InitializeOriginalBlackHoles(Path.Combine(Path.GetDirectoryName(shader)!,"GalaxyBlackHole.hlsl")); InitializeOriginalPowerActivityRing(shader); InitializeOriginalPowerActivityBillboard(shader); InitializeOriginalMilkyWay(shader); InitializeOriginalGrid(shader); InitializeOriginalCurrentTrade(shader); InitializeOriginalCore(Path.Combine(Path.GetDirectoryName(shader)!,"GalaxyCore.hlsl")); InitializeOriginalHdr(Path.Combine(Path.GetDirectoryName(shader)!,"GalaxyHdr.hlsl")); InitializeOriginalExposure(shader); InitializeOriginalPlane(Path.Combine(Path.GetDirectoryName(shader)!,"GalaxyPlane.hlsl")); var a=Compiler.CompileFromFile(shader,"VSStar","vs_5_0");starsVs=device.CreateVertexShader(a.Span); starsLayout=device.CreateInputLayout([ new InputElementDescription("POSITION",0,Format.R32G32_Float,0,0,InputClassification.PerInstanceData,1), new InputElementDescription("RADIUS",0,Format.R32G32_Float,8,0,InputClassification.PerInstanceData,1), new InputElementDescription("COLOR",0,Format.R32G32B32A32_Float,16,0,InputClassification.PerInstanceData,1), new InputElementDescription("APPEARANCE",0,Format.R32G32B32A32_Float,32,0,InputClassification.PerInstanceData,1)],a.Span); var b=Compiler.CompileFromFile(shader,"VSLine","vs_5_0");linesVs=device.CreateVertexShader(b.Span); linesLayout=device.CreateInputLayout([ new InputElementDescription("POSITION",0,Format.R32G32_Float,0,0), new InputElementDescription("TEXCOORD",0,Format.R32G32_Float,8,0), new InputElementDescription("COLOR",0,Format.R32G32B32A32_Float,16,0)],b.Span); starsPs=device.CreatePixelShader(Compiler.CompileFromFile(shader,"PSStar","ps_5_0").Span); linesPs=device.CreatePixelShader(Compiler.CompileFromFile(shader,"PSLine","ps_5_0").Span); skyVs=device.CreateVertexShader(Compiler.CompileFromFile(shader,"VSSky","vs_5_0").Span); skyPs=device.CreatePixelShader(Compiler.CompileFromFile(shader,"PSSky","ps_5_0").Span); inspectSkyPs=device.CreatePixelShader(Compiler.CompileFromFile(shader,"PSInspectSky","ps_5_0").Span); overviewVs=device.CreateVertexShader(Compiler.CompileFromFile(shader,"VSOverview","vs_5_0").Span); overviewPs=device.CreatePixelShader(Compiler.CompileFromFile(shader,"PSOverview","ps_5_0").Span); additive=device.CreateBlendState(new BlendDescription(Blend.One,Blend.InverseSourceAlpha)); sampler=device.CreateSamplerState(new SamplerDescription(Filter.MinMagMipLinear,TextureAddressMode.Clamp,TextureAddressMode.Clamp,TextureAddressMode.Clamp)); rasterizer=device.CreateRasterizerState(new RasterizerDescription(CullMode.None,FillMode.Solid)); noDepth=device.CreateDepthStencilState(new DepthStencilDescription(false,DepthWriteMask.Zero,ComparisonFunction.Always)); } private void Resize(int w,int h) { if(width==w&&height==h)return; context.OMSetRenderTargets(Array.Empty());target?.Dispose();pixels?.Dispose(); Marshal.ThrowExceptionForHR(resizeDraw(interop.NativePointer,new(w,h)));width=w;height=h; pixels=device.CreateTexture2D(Format.B8G8R8A8_UNorm,(uint)w,(uint)h,mipLevels:1,bindFlags:BindFlags.RenderTarget); target=device.CreateRenderTargetView(pixels); } private unsafe void Upload(ID3D11Buffer buffer,ReadOnlySpan data) where T:unmanaged { if(data.Length==0)return; var mapped=context.Map(buffer,0,MapMode.WriteDiscard); try{var bytes=MemoryMarshal.AsBytes(data);bytes.CopyTo(new Span((void*)mapped.DataPointer,bytes.Length));} finally{context.Unmap(buffer,0);} } public void Draw(ReadOnlySpan stars,ReadOnlySpan lines,double w,double h,double raster,double yaw,double pitch,Vector3 centre,float scale,float panX,float panY,bool realistic=true,bool showBackground=true,bool showUnresolvedStars=true,bool navigationMarkers=true) { UnresolvedStarBackgroundEnabled=showBackground&&showUnresolvedStars; Resize(Math.Clamp((int)Math.Ceiling(w*raster),1,7680),Math.Clamp((int)Math.Ceiling(h*raster),1,4320)); RefreshSky(); PrepareNebulae(w,h,yaw,pitch,centre,scale,panX,panY,showBackground); var backgroundCamera=GalaxyCamera.Create(new(centre.X,centre.Y,centre.Z),w,h,yaw,pitch,scale,panX,panY); // Pass the same recovered Core lens/clip values to every world pass. // optionsZW and densityExtentZW were unused in the normal map b0. var camera=new Camera(new((float)w,(float)h,(float)yaw,(float)pitch),new(sky is null?0:1,density is null?0:1,(float)backgroundCamera.FocalLength,(float)backgroundCamera.Distance), new(centre,scale),new(panX,panY,(float)(densityMap?.OriginX??0),(float)(densityMap?.OriginZ??0)), new((float)(densityMap?.ExtentX??1),(float)(densityMap?.ExtentZ??1),(float)backgroundCamera.Near,(float)backgroundCamera.ProjectionFar)); context.UpdateSubresource(in camera,constants);context.VSSetConstantBuffer(0,constants);context.PSSetConstantBuffer(0,constants); context.OMSetRenderTargets(target!);context.RSSetViewport(new Viewport(width,height));context.RSSetState(rasterizer);context.OMSetDepthStencilState(noDepth);context.OMSetBlendState(null); context.ClearRenderTargetView(target!,new Color4(.011f,.014f,.019f,1));DrawCalls=0; // The captured cockpit cube is only 256px/face and includes the local // resolved star field. Magnifying it here creates ten-pixel square stars // and duplicates catalogue stars. Keep it available for resource // inspection, but draw map stars exclusively from the real catalogue. context.OMSetBlendState(additive); OriginalSceneHdrDrawn=false; OriginalBackgroundHdrDrawn=false; BeginOriginalHdr(width,height,new Color4(0,0,0,1)); BindOriginalSourceOutput(GalaxyOriginalHdr.WriteScale,false); if(showBackground) { DrawOriginalPlane(backgroundCamera,true,GalaxyOriginalHdr.WriteScale); DrawOriginalCore(backgroundCamera,true,GalaxyOriginalHdr.WriteScale,GalaxyOriginalHdr.ReadScale); DrawOriginalMilkyWay(backgroundCamera,true,GalaxyOriginalHdr.WriteScale); OriginalBackgroundHdrDrawn=true; } if(showBackground&&showUnresolvedStars&&!OriginalMilkyWayReady&&density is not null&&scale<.2f) { context.IASetInputLayout(null);context.IASetPrimitiveTopology(PrimitiveTopology.TriangleList); context.VSSetShader(overviewVs);context.PSSetShader(overviewPs);context.PSSetShaderResource(1,density);context.PSSetSampler(0,sampler);context.Draw(6,0); context.PSSetShaderResource(1,null!);DrawCalls++; } // The legacy nebula/zone paths explicitly adapt RGB to scene storage; // their source materials remain incomplete. Recovered passes below // use their actual RGB-only Write and independent alpha/blend state. DrawNebulae(originalHdrTarget); DrawGalacticZones(); DrawPowerContours(); ComputeOriginalManualExposure(); DrawOriginalPowerActivityBillboard(backgroundCamera,GalaxyOriginalHdr.WriteScale); DrawOriginalPowerActivityRing(backgroundCamera,GalaxyOriginalHdr.WriteScale); DrawOriginalGrid(backgroundCamera,GalaxyOriginalHdr.WriteScale); DrawOriginalCurrentTrade(backgroundCamera,GalaxyOriginalHdr.WriteScale); if(stars.Length>0) { BindOriginalSourceOutput(GalaxyOriginalHdr.WriteScale); Upload(starBuffer,PrepareStellarSprites(ApplyNebulaTransmission(stars),backgroundCamera,realistic));context.IASetInputLayout(starsLayout);context.IASetPrimitiveTopology(PrimitiveTopology.TriangleList); context.IASetVertexBuffer(0,starBuffer,48);context.VSSetShader(starsVs);context.PSSetShader(starsPs); context.PSSetShaderResource(7,originalSprites!);context.PSSetSampler(0,sampler); context.DrawInstanced(6,(uint)stars.Length,0,0);context.PSSetShaderResource(7,null!);DrawCalls++; } DrawOriginalBlackHoles(stars,backgroundCamera,realistic,navigationMarkers,GalaxyOriginalHdr.WriteScale); EndOriginalHdr(target!);context.OMSetBlendState(additive);OriginalSceneHdrDrawn=true; // Tool navigation strokes are screen overlays, after scene tone mapping. if(lines.Length>0) { Upload(lineBuffer,lines);context.IASetInputLayout(linesLayout);context.IASetPrimitiveTopology(PrimitiveTopology.LineList); context.IASetVertexBuffer(0,lineBuffer,32);context.VSSetShader(linesVs);context.PSSetShader(linesPs);context.Draw((uint)lines.Length,0);DrawCalls++; } context.OMSetRenderTargets(Array.Empty()); var iid=typeof(ID3D11Texture2D).GUID;Marshal.ThrowExceptionForHR(beginDraw(interop.NativePointer,IntPtr.Zero,in iid,out var texture,out var offset)); using(var destination=new ID3D11Texture2D(texture)) { try{context.CopySubresourceRegion(destination,0,(uint)offset.X,(uint)offset.Y,0,pixels!,0,new Box(0,0,0,width,height,1));context.Flush();} finally{interop.EndDraw().CheckError();} } Frames++; } private ReadOnlySpan PrepareStellarSprites(ReadOnlySpan stars,GalaxyCamera camera,bool realistic) { var focus=camera.Focus;var forward=camera.Forward;var eye=camera.Eye; for(var i=0;i