using System.Numerics; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text.Json; using Vortice.Direct3D; using Vortice.Direct3D11; using Vortice.DXGI; using Vortice.Mathematics; using XFEEDToolBox.Core.Galaxy; using XFEEDToolBox.Core.Navigation; using XFEEDToolBox.Core.Rendering; using XFEEDToolBox.Rendering; using static Vortice.Direct3D11.D3D11; static void Require(bool condition,string text){if(!condition)throw new Exception(text);} Require(Marshal.SizeOf()==52,"Actual source instance must remain52B"); Require(GalaxyOriginalBlackHole.Payload.Length==181248&&GalaxyOriginalBlackHole.Mips.Count==10,"Exact source texture must be complete"); Require(GalaxyOriginalBlackHole.IsSpecial(41)&&GalaxyOriginalBlackHole.IsSpecial((ushort)(42|18<<10))&&!GalaxyOriginalBlackHole.IsSpecial(40),"Source types must not be guessed from public labels"); Require(GalaxyOriginalBlackHole.HalfExtent(.7f)==(float)(Half).7f&&GalaxyOriginalBlackHole.HalfExtent(.00001f)<.001f&& GalaxyOriginalBlackHole.HalfExtent(0)==0&&GalaxyOriginalBlackHole.HalfExtent(float.NaN)==0,"Original half conversion must have no positive size floor"); D3D11CreateDevice(IntPtr.Zero,DriverType.Warp,DeviceCreationFlags.BgraSupport,[FeatureLevel.Level_11_0],out var device,out _,out var context).CheckError(); using var d=device;using var c=context;using var renderer=new GalaxyGraphics(device,context); using var rasterizer=device.CreateRasterizerState(new RasterizerDescription(CullMode.None,FillMode.Solid)); using var noDepth=device.CreateDepthStencilState(new DepthStencilDescription(false,DepthWriteMask.Zero,ComparisonFunction.Always)); context.RSSetState(rasterizer);context.OMSetDepthStencilState(noDepth); const int width=480,height=300;var reports=new List();var maximumDifference=0f; var output=Path.GetFullPath("artifacts/galaxy-black-hole-shader-validation");Directory.CreateDirectory(output); foreach(var (view,distance,yaw,pitch,position) in new[]{("near",6d,0d,0d,new Vector3(0,0,6)), ("oblique",100d,.3,.45,new Vector3(1,2,3)),("far",800d,0d,0d,new Vector3(0,0,64))}) foreach(var dpi in new[]{1,2})foreach(var destinationAlpha in new[]{0f,1f})foreach(var write in new[]{1f,1f/256}) { var camera=GalaxyCamera.Create(new(0,0,0),width,height,yaw,pitch,GalaxyOriginalProjection.FocalLength(width,height)/distance); using var texture=device.CreateTexture2D(Format.R32G32B32A32_Float,(uint)(width*dpi),(uint)(height*dpi),mipLevels:1,bindFlags:BindFlags.RenderTarget); using var target=device.CreateRenderTargetView(texture); var projected=camera.ProjectForCatalogue(new(position.X,position.Y,position.Z)); Require(projected.IsVisible,"Explicit fixture must lie in catalogue envelope"); var star=new GalaxyStar(new((float)projected.X,(float)projected.Y),1,0,Vector4.One,new(position,13),.7f,OriginalBlackHole:true); var clear=new Color4(.04f,.08f,.12f,destinationAlpha); context.OMSetRenderTargets(target);context.RSSetViewport(new Viewport(width*dpi,height*dpi));context.ClearRenderTargetView(target,clear); renderer.Draw([star],camera,true,true,write);var actual=Read(texture); context.OMSetRenderTargets(target);context.ClearRenderTargetView(target,clear);renderer.DrawOriginalDxbc(camera,write);var original=Read(texture); float maximum=0;double total=0;var changed=0;var nonfinite=0;var negative=0; for(var i=0;iclear.R,1=>clear.G,2=>clear.B,_=>clear.A}; if(Math.Abs(original[i]-initial)>1e-7)changed++; if(original[i]<0)negative++; } Require(nonfinite==0&&total/actual.Length<.0001&&maximum<.05f,$"Original BlackHole DXBC mismatch {view}/dpi{dpi}/alpha{destinationAlpha}/write{write}: max={maximum},mean={total/actual.Length}"); maximumDifference=Math.Max(maximumDifference,maximum); reports.Add(new{view,dpi,destinationAlpha,write,sourceInstances=renderer.OriginalBlackHolesDrawn,sourceWorldHalfSize=(float)(Half).7f, projectedHalfSize=.7*camera.FocalLength/projected.Depth,maximumDifference=maximum,meanDifference=total/actual.Length,changedChannels=changed,negativeChannels=negative,nonfinite}); if(dpi==1) { var cut=$"{view}-destination-alpha{destinationAlpha}-write{(write==1?"1":"1over256")}"; File.WriteAllBytes(Path.Combine(output,cut+".r32"),MemoryMarshal.AsBytes(actual.AsSpan()).ToArray()); var image=new byte[width*height*4]; for(var i=0;i());var desc=texture.Description;desc.BindFlags=BindFlags.None;desc.CPUAccessFlags=CpuAccessFlags.Read;desc.Usage=ResourceUsage.Staging; using var staging=device.CreateTexture2D(desc);context.CopyResource(staging,texture);var mapped=context.Map(staging,0,MapMode.Read);var values=new float[desc.Width*desc.Height*4]; try{for(var y=0;ynew[]{(Half)v.X,(Half)v.Y,(Half)v.Z,(Half)v.W}).ToArray();sourceVertices=device.CreateBuffer(halves,BindFlags.VertexBuffer); sourceCamera=device.CreateBuffer(8*16,BindFlags.ConstantBuffer);sourceShared=device.CreateBuffer(279*16,BindFlags.ConstantBuffer);sourceMaterial=device.CreateBuffer(16,BindFlags.ConstantBuffer); } internal void Draw(ReadOnlySpan stars,GalaxyCamera camera,bool realistic,bool navigation,float write)=>DrawOriginalBlackHoles(stars,camera,realistic,navigation,write); internal void DrawOriginalDxbc(GalaxyCamera camera,float write) { Vector3 V(Vector3d v)=>new((float)v.X,(float)v.Y,(float)v.Z); var r=V(camera.Right);var up=V(camera.Up);var f=V(camera.Forward);var eye=new Vector3((float)camera.Eye.X,(float)camera.Eye.Y,(float)camera.Eye.Z); Vector4 Row(Vector3 direction,float scale)=>new(direction*scale,-Vector3.Dot(eye,direction)*scale); var projection=new Vector4[8];projection[4]=Row(r,(float)(camera.FocalLength/(camera.Width*.5)));projection[5]=Row(up,(float)(camera.FocalLength/(camera.Height*.5))); projection[6]=new(0,0,0,(float)camera.Near);projection[7]=Row(f,1); var shared=new Vector4[279];shared[90]=new(0,write,0,0);shared[277]=new(r,0);shared[278]=new(up,0); var material=GalaxyOriginalBlackHole.Material();context.UpdateSubresource(projection,sourceCamera);context.UpdateSubresource(shared,sourceShared);context.UpdateSubresource(in material,sourceMaterial); context.VSSetConstantBuffer(0,sourceCamera);context.VSSetConstantBuffer(1,sourceShared);context.VSSetConstantBuffer(2,sourceMaterial); context.PSSetConstantBuffer(1,sourceShared);context.PSSetConstantBuffer(2,sourceMaterial);context.PSSetShaderResource(0,blackHoleView);context.PSSetSampler(0,sampler); context.IASetInputLayout(sourceLayout);context.IASetPrimitiveTopology(PrimitiveTopology.TriangleList);context.IASetVertexBuffer(0,sourceVertices,8);context.IASetVertexBuffer(1,blackHoleBuffer,52); context.VSSetShader(sourceVs);context.PSSetShader(sourcePs);context.OMSetBlendState(blackHoleBlend);context.DrawInstanced(6,(uint)OriginalBlackHolesDrawn,0,0);context.PSSetShaderResource(0,null!); } private unsafe void Upload(ID3D11Buffer buffer,ReadOnlySpan data)where T:unmanaged {var map=context.Map(buffer,0,MapMode.WriteDiscard);try{MemoryMarshal.AsBytes(data).CopyTo(new Span((void*)map.DataPointer,data.Length*Marshal.SizeOf()));}finally{context.Unmap(buffer,0);}} public void Dispose(){DisposeOriginalBlackHoles();sampler.Dispose();sourceVs.Dispose();sourcePs.Dispose();sourceLayout.Dispose();sourceVertices.Dispose();sourceCamera.Dispose();sourceShared.Dispose();sourceMaterial.Dispose();} } }