private void UploadTexture(NvGpuVmm Vmm, long BasePosition, int TexIndex, int HndIndex) { long Position = BasePosition + HndIndex * 4; int TextureHandle = Vmm.ReadInt32(Position); int TicIndex = (TextureHandle >> 0) & 0xfffff; int TscIndex = (TextureHandle >> 20) & 0xfff; long TicPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.TexHeaderPoolOffset); long TscPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.TexSamplerPoolOffset); TicPosition += TicIndex * 0x20; TscPosition += TscIndex * 0x20; GalTextureSampler Sampler = TextureFactory.MakeSampler(Gpu, Vmm, TscPosition); long TextureAddress = Vmm.ReadInt64(TicPosition + 4) & 0xffffffffffff; long Tag = TextureAddress; TextureAddress = Vmm.GetPhysicalAddress(TextureAddress); if (IsFrameBufferPosition(TextureAddress)) { //This texture is a frame buffer texture, //we shouldn't read anything from memory and bind //the frame buffer texture instead, since we're not //really writing anything to memory. Gpu.Renderer.BindFrameBufferTexture(TextureAddress, TexIndex, Sampler); } else { GalTexture NewTexture = TextureFactory.MakeTexture(Vmm, TicPosition); long Size = (uint)TextureHelper.GetTextureSize(NewTexture); if (Gpu.Renderer.TryGetCachedTexture(Tag, Size, out GalTexture Texture)) { if (NewTexture.Equals(Texture) && !Vmm.IsRegionModified(Tag, Size, NvGpuBufferType.Texture)) { Gpu.Renderer.BindTexture(Tag, TexIndex); return; } } byte[] Data = TextureFactory.GetTextureData(Vmm, TicPosition); Gpu.Renderer.SetTextureAndSampler(Tag, Data, NewTexture, Sampler); Gpu.Renderer.BindTexture(Tag, TexIndex); } }
private static int[] ReadWords(NvGpuVmm Vmm, long Position, int Count) { int[] Words = new int[Count]; for (int Index = 0; Index < Count; Index++, Position += 4) { Words[Index] = Vmm.ReadInt32(Position); } return(Words); }
public void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) { if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method)) { Method(Vmm, PBEntry); } else { WriteRegister(PBEntry); } }
private void VertexEndGl(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) { SetFrameBuffer(Vmm, 0); long[] Tags = UploadShaders(Vmm); Gpu.Renderer.BindProgram(); SetAlphaBlending(); UploadTextures(Vmm, Tags); UploadUniforms(Vmm); UploadVertexArrays(Vmm); }
private void ClearBuffers(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) { int Arg0 = PBEntry.Arguments[0]; int FbIndex = (Arg0 >> 6) & 0xf; int Layer = (Arg0 >> 10) & 0x3ff; GalClearBufferFlags Flags = (GalClearBufferFlags)(Arg0 & 0x3f); SetFrameBuffer(Vmm, 0); //TODO: Enable this once the frame buffer problems are fixed. //Gpu.Renderer.ClearBuffers(Layer, Flags); }
private void CbData(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) { long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress); int Offset = ReadRegister(NvGpuEngine3dReg.ConstBufferOffset); foreach (int Arg in PBEntry.Arguments) { Vmm.WriteInt32(Position + Offset, Arg); Offset += 4; } WriteRegister(NvGpuEngine3dReg.ConstBufferOffset, Offset); }
private void SetFrameBuffer(NvGpuVmm Vmm, int FbIndex) { long VA = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + FbIndex * 0x10); long PA = Vmm.GetPhysicalAddress(VA); FrameBuffers.Add(PA); int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10); int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10); //Note: Using the Width/Height results seems to give incorrect results. //Maybe the size of all frame buffers is hardcoded to screen size? This seems unlikely. Gpu.Renderer.CreateFrameBuffer(PA, 1280, 720); Gpu.Renderer.BindFrameBuffer(PA); }
private void CbBind(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) { int Stage = (PBEntry.Method - 0x904) >> 3; int Index = PBEntry.Arguments[0]; bool Enabled = (Index & 1) != 0; Index = (Index >> 4) & 0x1f; long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress); ConstBuffers[Stage][Index].Position = Position; ConstBuffers[Stage][Index].Enabled = Enabled; ConstBuffers[Stage][Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferSize); }
private void QueryControl(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) { long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.QueryAddress); int Seq = Registers[(int)NvGpuEngine3dReg.QuerySequence]; int Ctrl = Registers[(int)NvGpuEngine3dReg.QueryControl]; int Mode = Ctrl & 3; if (Mode == 0) { //Write mode. Vmm.WriteInt32(Position, Seq); } WriteRegister(PBEntry); }
public void Execute(NvGpuVmm Vmm, int[] Mme, int Position, int Param) { Reset(); Gprs[1] = Param; Pc = Position; FetchOpCode(Mme); while (Step(Vmm, Mme)) { ; } //Due to the delay slot, we still need to execute //one more instruction before we actually exit. Step(Vmm, Mme); }
private long[] UploadShaders(NvGpuVmm Vmm) { long[] Tags = new long[5]; long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); for (int Index = 0; Index < 6; Index++) { int Control = ReadRegister(NvGpuEngine3dReg.ShaderNControl + Index * 0x10); int Offset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + Index * 0x10); //Note: Vertex Program (B) is always enabled. bool Enable = (Control & 1) != 0 || Index == 1; if (!Enable) { continue; } long Tag = BasePosition + (uint)Offset; GalShaderType ShaderType = GetTypeFromProgram(Index); Tags[(int)ShaderType] = Tag; Gpu.Renderer.CreateShader(Vmm, Tag, ShaderType); Gpu.Renderer.BindShader(Tag); } int RawSX = ReadRegister(NvGpuEngine3dReg.ViewportScaleX); int RawSY = ReadRegister(NvGpuEngine3dReg.ViewportScaleY); float SX = BitConverter.Int32BitsToSingle(RawSX); float SY = BitConverter.Int32BitsToSingle(RawSY); float SignX = MathF.Sign(SX); float SignY = MathF.Sign(SY); Gpu.Renderer.SetUniform2F(GalConsts.FlipUniformName, SignX, SignY); return(Tags); }
public static GalTexture MakeTexture(NvGpuVmm Vmm, long TicPosition) { int[] Tic = ReadWords(Vmm, TicPosition, 8); GalTextureFormat Format = (GalTextureFormat)(Tic[0] & 0x7f); GalTextureSource XSource = (GalTextureSource)((Tic[0] >> 19) & 7); GalTextureSource YSource = (GalTextureSource)((Tic[0] >> 22) & 7); GalTextureSource ZSource = (GalTextureSource)((Tic[0] >> 25) & 7); GalTextureSource WSource = (GalTextureSource)((Tic[0] >> 28) & 7); int Width = (Tic[4] & 0xffff) + 1; int Height = (Tic[5] & 0xffff) + 1; return(new GalTexture( Width, Height, Format, XSource, YSource, ZSource, WSource)); }
private void UploadTextures(NvGpuVmm Vmm, long[] Tags) { long BaseShPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); int TextureCbIndex = ReadRegister(NvGpuEngine3dReg.TextureCbIndex); //Note: On the emulator renderer, Texture Unit 0 is //reserved for drawing the frame buffer. int TexIndex = 1; for (int Index = 0; Index < Tags.Length; Index++) { foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.GetTextureUsage(Tags[Index])) { long Position = ConstBuffers[Index][TextureCbIndex].Position; UploadTexture(Vmm, Position, TexIndex, DeclInfo.Index); Gpu.Renderer.SetUniform1(DeclInfo.Name, TexIndex); TexIndex++; } } }
private void Execute(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) { int Control = PBEntry.Arguments[0]; bool SrcLinear = ((Control >> 7) & 1) != 0; bool DstLinear = ((Control >> 8) & 1) != 0; long SrcAddress = MakeInt64From2xInt32(NvGpuEngineDmaReg.SrcAddress); long DstAddress = MakeInt64From2xInt32(NvGpuEngineDmaReg.DstAddress); int SrcPitch = ReadRegister(NvGpuEngineDmaReg.SrcPitch); int DstPitch = ReadRegister(NvGpuEngineDmaReg.DstPitch); int DstBlkDim = ReadRegister(NvGpuEngineDmaReg.DstBlkDim); int DstSizeX = ReadRegister(NvGpuEngineDmaReg.DstSizeX); int DstSizeY = ReadRegister(NvGpuEngineDmaReg.DstSizeY); int DstSizeZ = ReadRegister(NvGpuEngineDmaReg.DstSizeZ); int DstPosXY = ReadRegister(NvGpuEngineDmaReg.DstPosXY); int DstPosZ = ReadRegister(NvGpuEngineDmaReg.DstPosZ); int SrcBlkDim = ReadRegister(NvGpuEngineDmaReg.SrcBlkDim); int SrcSizeX = ReadRegister(NvGpuEngineDmaReg.SrcSizeX); int SrcSizeY = ReadRegister(NvGpuEngineDmaReg.SrcSizeY); int SrcSizeZ = ReadRegister(NvGpuEngineDmaReg.SrcSizeZ); int SrcPosXY = ReadRegister(NvGpuEngineDmaReg.SrcPosXY); int SrcPosZ = ReadRegister(NvGpuEngineDmaReg.SrcPosZ); int DstPosX = (DstPosXY >> 0) & 0xffff; int DstPosY = (DstPosXY >> 16) & 0xffff; int SrcPosX = (SrcPosXY >> 0) & 0xffff; int SrcPosY = (SrcPosXY >> 16) & 0xffff; int SrcBlockHeight = 1 << ((SrcBlkDim >> 4) & 0xf); int DstBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf); ISwizzle SrcSwizzle; if (SrcLinear) { SrcSwizzle = new LinearSwizzle(SrcPitch, 1); } else { SrcSwizzle = new BlockLinearSwizzle(SrcSizeX, 1, SrcBlockHeight); } ISwizzle DstSwizzle; if (DstLinear) { DstSwizzle = new LinearSwizzle(DstPitch, 1); } else { DstSwizzle = new BlockLinearSwizzle(DstSizeX, 1, DstBlockHeight); } for (int Y = 0; Y < DstSizeY; Y++) { for (int X = 0; X < DstSizeX; X++) { long SrcOffset = SrcAddress + (uint)SrcSwizzle.GetSwizzleOffset(X, Y); long DstOffset = DstAddress + (uint)DstSwizzle.GetSwizzleOffset(X, Y); Vmm.WriteByte(DstOffset, Vmm.ReadByte(SrcOffset)); } } }
private bool Step(NvGpuVmm Vmm, int[] Mme) { int BaseAddr = Pc - 1; FetchOpCode(Mme); if ((OpCode & 7) < 7) { //Operation produces a value. AssignmentOperation AsgOp = (AssignmentOperation)((OpCode >> 4) & 7); int Result = GetAluResult(); switch (AsgOp) { //Fetch parameter and ignore result. case AssignmentOperation.IgnoreAndFetch: { SetDstGpr(FetchParam()); break; } //Move result. case AssignmentOperation.Move: { SetDstGpr(Result); break; } //Move result and use as Method Address. case AssignmentOperation.MoveAndSetMaddr: { SetDstGpr(Result); SetMethAddr(Result); break; } //Fetch parameter and send result. case AssignmentOperation.FetchAndSend: { SetDstGpr(FetchParam()); Send(Vmm, Result); break; } //Move and send result. case AssignmentOperation.MoveAndSend: { SetDstGpr(Result); Send(Vmm, Result); break; } //Fetch parameter and use result as Method Address. case AssignmentOperation.FetchAndSetMaddr: { SetDstGpr(FetchParam()); SetMethAddr(Result); break; } //Move result and use as Method Address, then fetch and send paramter. case AssignmentOperation.MoveAndSetMaddrThenFetchAndSend: { SetDstGpr(Result); SetMethAddr(Result); Send(Vmm, FetchParam()); break; } //Move result and use as Method Address, then send bits 17:12 of result. case AssignmentOperation.MoveAndSetMaddrThenSendHigh: { SetDstGpr(Result); SetMethAddr(Result); Send(Vmm, (Result >> 12) & 0x3f); break; } } } else { //Branch. bool OnNotZero = ((OpCode >> 4) & 1) != 0; bool Taken = OnNotZero ? GetGprA() != 0 : GetGprA() == 0; if (Taken) { Pc = BaseAddr + GetImm(); bool NoDelays = (OpCode & 0x20) != 0; if (NoDelays) { FetchOpCode(Mme); } return(true); } } bool Exit = (OpCode & 0x80) != 0; return(!Exit); }
private void UploadVertexArrays(NvGpuVmm Vmm) { long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); int IndexSize = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); int IndexFirst = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst); int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount); GalIndexFormat IndexFormat = (GalIndexFormat)IndexSize; IndexSize = 1 << IndexSize; if (IndexSize > 4) { throw new InvalidOperationException(); } if (IndexSize != 0) { int IbSize = IndexCount * IndexSize; bool IboCached = Gpu.Renderer.IsIboCached(IndexPosition, (uint)IbSize); if (!IboCached || Vmm.IsRegionModified(IndexPosition, (uint)IbSize, NvGpuBufferType.Index)) { byte[] Data = Vmm.ReadBytes(IndexPosition, (uint)IbSize); Gpu.Renderer.CreateIbo(IndexPosition, Data); } Gpu.Renderer.SetIndexArray(IndexPosition, IbSize, IndexFormat); } List <GalVertexAttrib>[] Attribs = new List <GalVertexAttrib> [32]; for (int Attr = 0; Attr < 16; Attr++) { int Packed = ReadRegister(NvGpuEngine3dReg.VertexAttribNFormat + Attr); int ArrayIndex = Packed & 0x1f; if (Attribs[ArrayIndex] == null) { Attribs[ArrayIndex] = new List <GalVertexAttrib>(); } Attribs[ArrayIndex].Add(new GalVertexAttrib( Attr, ((Packed >> 6) & 0x1) != 0, (Packed >> 7) & 0x3fff, (GalVertexAttribSize)((Packed >> 21) & 0x3f), (GalVertexAttribType)((Packed >> 27) & 0x7), ((Packed >> 31) & 0x1) != 0)); } int VertexFirst = ReadRegister(NvGpuEngine3dReg.VertexArrayFirst); int VertexCount = ReadRegister(NvGpuEngine3dReg.VertexArrayCount); int PrimCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl); for (int Index = 0; Index < 32; Index++) { if (Attribs[Index] == null) { continue; } int Control = ReadRegister(NvGpuEngine3dReg.VertexArrayNControl + Index * 4); bool Enable = (Control & 0x1000) != 0; long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4); long VertexEndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 2); if (!Enable) { continue; } int Stride = Control & 0xfff; long VbSize = 0; if (IndexCount != 0) { VbSize = (VertexEndPos - VertexPosition) + 1; } else { VbSize = VertexCount * Stride; } bool VboCached = Gpu.Renderer.IsVboCached(VertexPosition, VbSize); if (!VboCached || Vmm.IsRegionModified(VertexPosition, VbSize, NvGpuBufferType.Vertex)) { byte[] Data = Vmm.ReadBytes(VertexPosition, VbSize); Gpu.Renderer.CreateVbo(VertexPosition, Data); } Gpu.Renderer.SetVertexArray(Index, Stride, VertexPosition, Attribs[Index].ToArray()); } GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff); if (IndexCount != 0) { Gpu.Renderer.DrawElements(IndexPosition, IndexFirst, PrimType); } else { Gpu.Renderer.DrawArrays(VertexFirst, VertexCount, PrimType); } }
private void TextureCopy(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) { CopyOperation Operation = (CopyOperation)ReadRegister(NvGpuEngine2dReg.CopyOperation); bool SrcLinear = ReadRegister(NvGpuEngine2dReg.SrcLinear) != 0; int SrcWidth = ReadRegister(NvGpuEngine2dReg.SrcWidth); int SrcHeight = ReadRegister(NvGpuEngine2dReg.SrcHeight); bool DstLinear = ReadRegister(NvGpuEngine2dReg.DstLinear) != 0; int DstWidth = ReadRegister(NvGpuEngine2dReg.DstWidth); int DstHeight = ReadRegister(NvGpuEngine2dReg.DstHeight); int DstPitch = ReadRegister(NvGpuEngine2dReg.DstPitch); int DstBlkDim = ReadRegister(NvGpuEngine2dReg.DstBlockDimensions); TextureSwizzle DstSwizzle = DstLinear ? TextureSwizzle.Pitch : TextureSwizzle.BlockLinear; int DstBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf); long Tag = Vmm.GetPhysicalAddress(MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress)); long SrcAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress); long DstAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.DstAddress); bool IsFbTexture = Gpu.Engine3d.IsFrameBufferPosition(Tag); if (IsFbTexture && DstLinear) { DstSwizzle = TextureSwizzle.BlockLinear; } Texture DstTexture = new Texture( DstAddress, DstWidth, DstHeight, DstBlockHeight, DstBlockHeight, DstSwizzle, GalTextureFormat.A8B8G8R8); if (IsFbTexture) { //TODO: Change this when the correct frame buffer resolution is used. //Currently, the frame buffer size is hardcoded to 1280x720. SrcWidth = 1280; SrcHeight = 720; Gpu.Renderer.GetFrameBufferData(Tag, (byte[] Buffer) => { CopyTexture( Vmm, DstTexture, Buffer, SrcWidth, SrcHeight); }); } else { long Size = SrcWidth * SrcHeight * 4; byte[] Buffer = Vmm.ReadBytes(SrcAddress, Size); CopyTexture( Vmm, DstTexture, Buffer, SrcWidth, SrcHeight); } }