private void CbBind(NvGpuVmm vmm, GpuMethodCall methCall) { int stage = (methCall.Method - 0x904) >> 3; int index = methCall.Argument; bool enabled = (index & 1) != 0; index = (index >> 4) & 0x1f; long position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress); long cbKey = vmm.GetPhysicalAddress(position); int size = ReadRegister(NvGpuEngine3dReg.ConstBufferSize); if (!_gpu.Renderer.Buffer.IsCached(cbKey, size)) { _gpu.Renderer.Buffer.Create(cbKey, size); } ConstBuffer cb = _constBuffers[stage][index]; if (cb.Position != position || cb.Enabled != enabled || cb.Size != size) { _constBuffers[stage][index].Position = position; _constBuffers[stage][index].Enabled = enabled; _constBuffers[stage][index].Size = size; } }
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); long CbKey = Vmm.GetPhysicalAddress(Position); int Size = ReadRegister(NvGpuEngine3dReg.ConstBufferSize); if (!Gpu.Renderer.Buffer.IsCached(CbKey, Size)) { Gpu.Renderer.Buffer.Create(CbKey, Size); } ConstBuffer Cb = ConstBuffers[Stage][Index]; if (Cb.Position != Position || Cb.Enabled != Enabled || Cb.Size != Size) { ConstBuffers[Stage][Index].Position = Position; ConstBuffers[Stage][Index].Enabled = Enabled; ConstBuffers[Stage][Index].Size = Size; } }
private void SetZeta(NvGpuVmm Vmm) { long VA = MakeInt64From2xInt32(NvGpuEngine3dReg.ZetaAddress); int ZetaFormat = ReadRegister(NvGpuEngine3dReg.ZetaFormat); int BlockDim = ReadRegister(NvGpuEngine3dReg.ZetaBlockDimensions); int GobBlockHeight = 1 << ((BlockDim >> 4) & 7); GalMemoryLayout Layout = (GalMemoryLayout)((BlockDim >> 12) & 1); //? bool ZetaEnable = ReadRegisterBool(NvGpuEngine3dReg.ZetaEnable); if (VA == 0 || ZetaFormat == 0 || !ZetaEnable) { Gpu.Renderer.RenderTarget.UnbindZeta(); return; } long Key = Vmm.GetPhysicalAddress(VA); int Width = ReadRegister(NvGpuEngine3dReg.ZetaHoriz); int Height = ReadRegister(NvGpuEngine3dReg.ZetaVert); GalImageFormat Format = ImageUtils.ConvertZeta((GalZetaFormat)ZetaFormat); GalImage Image = new GalImage(Width, Height, 1, GobBlockHeight, Layout, Format); Gpu.ResourceManager.SendZetaBuffer(Vmm, Key, Image); }
private void UploadConstBuffers(NvGpuVmm Vmm, GalPipelineState State, long[] Keys) { for (int Stage = 0; Stage < Keys.Length; Stage++) { foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.Shader.GetConstBufferUsage(Keys[Stage])) { ConstBuffer Cb = ConstBuffers[Stage][DeclInfo.Cbuf]; if (!Cb.Enabled) { continue; } long Key = Vmm.GetPhysicalAddress(Cb.Position); if (Gpu.ResourceManager.MemoryRegionModified(Vmm, Key, Cb.Size, NvGpuBufferType.ConstBuffer)) { if (Vmm.TryGetHostAddress(Cb.Position, Cb.Size, out IntPtr CbPtr)) { Gpu.Renderer.Buffer.SetData(Key, Cb.Size, CbPtr); } else { Gpu.Renderer.Buffer.SetData(Key, Vmm.ReadBytes(Cb.Position, Cb.Size)); } } State.ConstBufferKeys[Stage][DeclInfo.Cbuf] = Key; } } }
private void SetZeta(NvGpuVmm vmm) { long va = MakeInt64From2xInt32(NvGpuEngine3dReg.ZetaAddress); int zetaFormat = ReadRegister(NvGpuEngine3dReg.ZetaFormat); int blockDim = ReadRegister(NvGpuEngine3dReg.ZetaBlockDimensions); int gobBlockHeight = 1 << ((blockDim >> 4) & 7); GalMemoryLayout layout = (GalMemoryLayout)((blockDim >> 12) & 1); //? bool zetaEnable = ReadRegisterBool(NvGpuEngine3dReg.ZetaEnable); if (va == 0 || zetaFormat == 0 || !zetaEnable) { _gpu.Renderer.RenderTarget.UnbindZeta(); return; } long key = vmm.GetPhysicalAddress(va); int width = ReadRegister(NvGpuEngine3dReg.ZetaHoriz); int height = ReadRegister(NvGpuEngine3dReg.ZetaVert); GalImageFormat format = ImageUtils.ConvertZeta((GalZetaFormat)zetaFormat); // TODO: Support non 2D? GalImage image = new GalImage(width, height, 1, 1, 1, gobBlockHeight, 1, layout, format, GalTextureTarget.TwoD); _gpu.ResourceManager.SendZetaBuffer(vmm, key, image); }
private void UploadConstBuffers(NvGpuVmm Vmm, GalPipelineState State, long[] Keys) { for (int Stage = 0; Stage < Keys.Length; Stage++) { foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.Shader.GetConstBufferUsage(Keys[Stage])) { ConstBuffer Cb = ConstBuffers[Stage][DeclInfo.Cbuf]; if (!Cb.Enabled) { continue; } long Key = Vmm.GetPhysicalAddress(Cb.Position); if (QueryKeyUpload(Vmm, Key, Cb.Size, NvGpuBufferType.ConstBuffer)) { IntPtr Source = Vmm.GetHostAddress(Cb.Position, Cb.Size); Gpu.Renderer.Buffer.SetData(Key, Cb.Size, Source); } State.ConstBufferKeys[Stage][DeclInfo.Cbuf] = Key; } } }
private void DispatchRender(NvGpuVmm Vmm, GalPipelineState State) { int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount); int PrimCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl); GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff); Gpu.Renderer.Pipeline.Bind(State); if (IndexCount != 0) { int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); int IndexFirst = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst); int VertexBase = ReadRegister(NvGpuEngine3dReg.VertexArrayElemBase); long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); long IboKey = Vmm.GetPhysicalAddress(IndexPosition); Gpu.Renderer.Rasterizer.DrawElements(IboKey, IndexFirst, VertexBase, PrimType); } else { int VertexFirst = ReadRegister(NvGpuEngine3dReg.VertexArrayFirst); int VertexCount = ReadRegister(NvGpuEngine3dReg.VertexArrayCount); Gpu.Renderer.Rasterizer.DrawArrays(VertexFirst, VertexCount, PrimType); } //Is the GPU really clearing those registers after draw? WriteRegister(NvGpuEngine3dReg.IndexBatchFirst, 0); WriteRegister(NvGpuEngine3dReg.IndexBatchCount, 0); }
private void SetZeta(NvGpuVmm Vmm) { long ZA = MakeInt64From2xInt32(NvGpuEngine3dReg.ZetaAddress); int Format = ReadRegister(NvGpuEngine3dReg.ZetaFormat); bool ZetaEnable = (ReadRegister(NvGpuEngine3dReg.ZetaEnable) & 1) != 0; if (ZA == 0 || Format == 0 || !ZetaEnable) { Gpu.Renderer.FrameBuffer.UnbindZeta(); return; } long Key = Vmm.GetPhysicalAddress(ZA); int Width = ReadRegister(NvGpuEngine3dReg.ZetaHoriz); int Height = ReadRegister(NvGpuEngine3dReg.ZetaVert); GalImageFormat ImageFormat = ImageFormatConverter.ConvertZeta((GalZetaFormat)Format); GalImage Image = new GalImage(Width, Height, ImageFormat); long Size = TextureHelper.GetTextureSize(Image); Gpu.Renderer.Texture.CreateFb(Key, Size, Image); Gpu.Renderer.FrameBuffer.BindZeta(Key); }
private void SetFrameBuffer(NvGpuVmm Vmm, int FbIndex) { long VA = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + FbIndex * 0x10); long Key = Vmm.GetPhysicalAddress(VA); FrameBuffers.Add(Key); int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10); int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10); float TX = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateX + FbIndex * 4); float TY = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateY + FbIndex * 4); float SX = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNScaleX + FbIndex * 4); float SY = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNScaleY + FbIndex * 4); int VpX = (int)MathF.Max(0, TX - MathF.Abs(SX)); int VpY = (int)MathF.Max(0, TY - MathF.Abs(SY)); int VpW = (int)(TX + MathF.Abs(SX)) - VpX; int VpH = (int)(TY + MathF.Abs(SY)) - VpY; Gpu.Renderer.FrameBuffer.Create(Key, Width, Height); Gpu.Renderer.FrameBuffer.Bind(Key); Gpu.Renderer.FrameBuffer.SetViewport(VpX, VpY, VpW, VpH); }
private void UploadConstBuffers(NvGpuVmm vmm, GalPipelineState state, long[] keys) { Profile.Begin(Profiles.GPU.Engine3d.UploadConstBuffers); for (int stage = 0; stage < keys.Length; stage++) { foreach (CBufferDescriptor desc in _gpu.Renderer.Shader.GetConstBufferUsage(keys[stage])) { ConstBuffer cb = _constBuffers[stage][desc.Slot]; if (!cb.Enabled) { continue; } long key = vmm.GetPhysicalAddress(cb.Position); if (_gpu.ResourceManager.MemoryRegionModified(vmm, key, cb.Size, NvGpuBufferType.ConstBuffer)) { if (vmm.TryGetHostAddress(cb.Position, cb.Size, out IntPtr cbPtr)) { _gpu.Renderer.Buffer.SetData(key, cb.Size, cbPtr); } else { _gpu.Renderer.Buffer.SetData(key, vmm.ReadBytes(cb.Position, cb.Size)); } } state.ConstBufferKeys[stage][desc.Slot] = key; } } Profile.End(Profiles.GPU.Engine3d.UploadConstBuffers); }
private byte[] ReadShaderBinary(NvGpuVmm Vmm, long Key) { long Size = GetShaderSize(Vmm, Key); long Address = Vmm.GetPhysicalAddress(Key); return(Vmm.ReadBytes(Key, Size)); }
private void SetFrameBuffer(NvGpuVmm vmm, int fbIndex) { ProfileConfig profile = Profiles.GPU.Engine3d.SetFrameBuffer; profile.SessionItem = fbIndex.ToString(); Profile.Begin(profile); long va = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + fbIndex * 0x10); int surfFormat = ReadRegister(NvGpuEngine3dReg.FrameBufferNFormat + fbIndex * 0x10); if (va == 0 || surfFormat == 0) { _gpu.Renderer.RenderTarget.UnbindColor(fbIndex); Profile.End(profile); return; } long key = vmm.GetPhysicalAddress(va); int width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + fbIndex * 0x10); int height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + fbIndex * 0x10); int arrayMode = ReadRegister(NvGpuEngine3dReg.FrameBufferNArrayMode + fbIndex * 0x10); int layerCount = arrayMode & 0xFFFF; int layerStride = ReadRegister(NvGpuEngine3dReg.FrameBufferNLayerStride + fbIndex * 0x10); int baseLayer = ReadRegister(NvGpuEngine3dReg.FrameBufferNBaseLayer + fbIndex * 0x10); int blockDim = ReadRegister(NvGpuEngine3dReg.FrameBufferNBlockDim + fbIndex * 0x10); int gobBlockHeight = 1 << ((blockDim >> 4) & 7); GalMemoryLayout layout = (GalMemoryLayout)((blockDim >> 12) & 1); float tx = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateX + fbIndex * 8); float ty = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateY + fbIndex * 8); float sx = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNScaleX + fbIndex * 8); float sy = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNScaleY + fbIndex * 8); _viewportX0 = (int)MathF.Max(0, tx - MathF.Abs(sx)); _viewportY0 = (int)MathF.Max(0, ty - MathF.Abs(sy)); _viewportX1 = (int)(tx + MathF.Abs(sx)); _viewportY1 = (int)(ty + MathF.Abs(sy)); GalImageFormat format = ImageUtils.ConvertSurface((GalSurfaceFormat)surfFormat); GalImage image = new GalImage(width, height, 1, 1, 1, gobBlockHeight, 1, layout, format, GalTextureTarget.TwoD); _gpu.ResourceManager.SendColorBuffer(vmm, key, fbIndex, image); _gpu.Renderer.RenderTarget.SetViewport(fbIndex, _viewportX0, _viewportY0, _viewportX1 - _viewportX0, _viewportY1 - _viewportY0); Profile.End(profile); }
private (long, GalImage, GalTextureSampler) UploadTexture(NvGpuVmm vmm, int textureHandle) { if (textureHandle == 0) { // FIXME: Some games like puyo puyo will use handles with the value 0. // This is a bug, most likely caused by sync issues. return(0, default(GalImage), default(GalTextureSampler)); } Profile.Begin(Profiles.GPU.Engine3d.UploadTexture); bool linkedTsc = ReadRegisterBool(NvGpuEngine3dReg.LinkedTsc); int ticIndex = (textureHandle >> 0) & 0xfffff; int tscIndex = linkedTsc ? ticIndex : (textureHandle >> 20) & 0xfff; long ticPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.TexHeaderPoolOffset); long tscPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.TexSamplerPoolOffset); ticPosition += ticIndex * 0x20; tscPosition += tscIndex * 0x20; GalImage image = TextureFactory.MakeTexture(vmm, ticPosition); GalTextureSampler sampler = TextureFactory.MakeSampler(_gpu, vmm, tscPosition); long key = vmm.ReadInt64(ticPosition + 4) & 0xffffffffffff; if (image.Layout == GalMemoryLayout.BlockLinear) { key &= ~0x1ffL; } else if (image.Layout == GalMemoryLayout.Pitch) { key &= ~0x1fL; } key = vmm.GetPhysicalAddress(key); if (key == -1) { Profile.End(Profiles.GPU.Engine3d.UploadTexture); // FIXME: Shouldn't ignore invalid addresses. return(0, default(GalImage), default(GalTextureSampler)); } _gpu.ResourceManager.SendTexture(vmm, key, image); Profile.End(Profiles.GPU.Engine3d.UploadTexture); return(key, image, sampler); }
private void DispatchRender(NvGpuVmm Vmm, GalPipelineState State) { int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount); int PrimCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl); GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff); bool InstanceNext = ((PrimCtrl >> 26) & 1) != 0; bool InstanceCont = ((PrimCtrl >> 27) & 1) != 0; if (InstanceNext && InstanceCont) { throw new InvalidOperationException("GPU tried to increase and reset instance count at the same time"); } if (InstanceNext) { CurrentInstance++; } else if (!InstanceCont) { CurrentInstance = 0; } State.Instance = CurrentInstance; Gpu.Renderer.Pipeline.Bind(State); Gpu.Renderer.RenderTarget.Bind(); if (IndexCount != 0) { int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); int IndexFirst = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst); int VertexBase = ReadRegister(NvGpuEngine3dReg.VertexArrayElemBase); long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); long IboKey = Vmm.GetPhysicalAddress(IndexPosition); Gpu.Renderer.Rasterizer.DrawElements(IboKey, IndexFirst, VertexBase, PrimType); } else { int VertexFirst = ReadRegister(NvGpuEngine3dReg.VertexArrayFirst); int VertexCount = ReadRegister(NvGpuEngine3dReg.VertexArrayCount); Gpu.Renderer.Rasterizer.DrawArrays(VertexFirst, VertexCount, PrimType); } //Is the GPU really clearing those registers after draw? WriteRegister(NvGpuEngine3dReg.IndexBatchFirst, 0); WriteRegister(NvGpuEngine3dReg.IndexBatchCount, 0); }
private bool IsShaderModified(NvGpuVmm Vmm, long Key) { long Address = Vmm.GetPhysicalAddress(Key); if (Gpu.Renderer.Shader.TryGetSize(Address, out long Size)) { if (!QueryKeyUpload(Vmm, Address, Size, NvGpuBufferType.Shader)) { return(false); } } return(true); }
private (long, GalImage, GalTextureSampler) UploadTexture(NvGpuVmm Vmm, int TextureHandle) { if (TextureHandle == 0) { //FIXME: Some games like puyo puyo will use handles with the value 0. //This is a bug, most likely caused by sync issues. return(0, default(GalImage), default(GalTextureSampler)); } bool LinkedTsc = ReadRegisterBool(NvGpuEngine3dReg.LinkedTsc); int TicIndex = (TextureHandle >> 0) & 0xfffff; int TscIndex = LinkedTsc ? TicIndex : (TextureHandle >> 20) & 0xfff; long TicPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.TexHeaderPoolOffset); long TscPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.TexSamplerPoolOffset); TicPosition += TicIndex * 0x20; TscPosition += TscIndex * 0x20; GalImage Image = TextureFactory.MakeTexture(Vmm, TicPosition); GalTextureSampler Sampler = TextureFactory.MakeSampler(Gpu, Vmm, TscPosition); long Key = Vmm.ReadInt64(TicPosition + 4) & 0xffffffffffff; if (Image.Layout == GalMemoryLayout.BlockLinear) { Key &= ~0x1ffL; } else if (Image.Layout == GalMemoryLayout.Pitch) { Key &= ~0x1fL; } Key = Vmm.GetPhysicalAddress(Key); if (Key == -1) { //FIXME: Shouldn't ignore invalid addresses. return(0, default(GalImage), default(GalTextureSampler)); } Gpu.ResourceManager.SendTexture(Vmm, Key, Image); return(Key, Image, Sampler); }
private void SetFrameBuffer(NvGpuVmm Vmm, int FbIndex) { long VA = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + FbIndex * 0x10); long Key = Vmm.GetPhysicalAddress(VA); FrameBuffers.Add(Key); 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.FrameBuffer.Create(Key, 1280, 720); Gpu.Renderer.FrameBuffer.Bind(Key); }
private void UploadTexture(NvGpuVmm Vmm, int TexIndex, int TextureHandle) { if (TextureHandle == 0) { //TODO: Is this correct? //Some games like puyo puyo will have 0 handles. //It may be just normal behaviour or a bug caused by sync issues. //The game does initialize the value properly after through. return; } 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; GalImage Image = TextureFactory.MakeTexture(Vmm, TicPosition); GalTextureSampler Sampler = TextureFactory.MakeSampler(Gpu, Vmm, TscPosition); long Key = Vmm.ReadInt64(TicPosition + 4) & 0xffffffffffff; if (Image.Layout == GalMemoryLayout.BlockLinear) { Key &= ~0x1ffL; } else if (Image.Layout == GalMemoryLayout.Pitch) { Key &= ~0x1fL; } Key = Vmm.GetPhysicalAddress(Key); if (Key == -1) { //FIXME: Shouldn't ignore invalid addresses. return; } Gpu.ResourceManager.SendTexture(Vmm, Key, Image, TexIndex); Gpu.Renderer.Texture.SetSampler(Sampler); }
private void SetFrameBuffer(NvGpuVmm Vmm, int FbIndex) { long VA = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + FbIndex * 0x10); int SurfFormat = ReadRegister(NvGpuEngine3dReg.FrameBufferNFormat + FbIndex * 0x10); if (VA == 0 || SurfFormat == 0) { Gpu.Renderer.RenderTarget.UnbindColor(FbIndex); return; } long Key = Vmm.GetPhysicalAddress(VA); int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10); int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10); int BlockDim = ReadRegister(NvGpuEngine3dReg.FrameBufferNBlockDim + FbIndex * 0x10); int GobBlockHeight = 1 << ((BlockDim >> 4) & 7); GalMemoryLayout Layout = (GalMemoryLayout)((BlockDim >> 12) & 1); float TX = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateX + FbIndex * 8); float TY = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateY + FbIndex * 8); float SX = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNScaleX + FbIndex * 8); float SY = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNScaleY + FbIndex * 8); int VpX = (int)MathF.Max(0, TX - MathF.Abs(SX)); int VpY = (int)MathF.Max(0, TY - MathF.Abs(SY)); int VpW = (int)(TX + MathF.Abs(SX)) - VpX; int VpH = (int)(TY + MathF.Abs(SY)) - VpY; GalImageFormat Format = ImageUtils.ConvertSurface((GalSurfaceFormat)SurfFormat); GalImage Image = new GalImage(Width, Height, 1, GobBlockHeight, Layout, Format); Gpu.ResourceManager.SendColorBuffer(Vmm, Key, FbIndex, Image); ViewportHeight = VpH; Gpu.Renderer.RenderTarget.SetViewport(FbIndex, VpX, VpY, VpW, VpH); }
private void CopyPlanesRgba8(NvGpuVmm Vmm, SurfaceOutputConfig OutputConfig) { FFmpegFrame Frame = FFmpegWrapper.GetFrameRgba(); if ((Frame.Width | Frame.Height) == 0) { return; } GalImage Image = new GalImage( OutputConfig.SurfaceWidth, OutputConfig.SurfaceHeight, 1, OutputConfig.GobBlockHeight, GalMemoryLayout.BlockLinear, GalImageFormat.RGBA8 | GalImageFormat.Unorm); ImageUtils.WriteTexture(Vmm, Image, Vmm.GetPhysicalAddress(OutputConfig.SurfaceLumaAddress), Frame.Data); }
private void CopyPlanesRgba8(NvGpuVmm vmm, SurfaceOutputConfig outputConfig) { FFmpegFrame frame = FFmpegWrapper.GetFrameRgba(); if ((frame.Width | frame.Height) == 0) { return; } GalImage image = new GalImage( outputConfig.SurfaceWidth, outputConfig.SurfaceHeight, 1, 1, 1, outputConfig.GobBlockHeight, 1, GalMemoryLayout.BlockLinear, GalImageFormat.Rgba8 | GalImageFormat.Unorm, GalTextureTarget.TwoD); ImageUtils.WriteTexture(vmm, image, vmm.GetPhysicalAddress(outputConfig.SurfaceLumaAddress), frame.Data); }
private void SetFrameBuffer(NvGpuVmm Vmm, int FbIndex) { long VA = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + FbIndex * 0x10); int Format = ReadRegister(NvGpuEngine3dReg.FrameBufferNFormat + FbIndex * 0x10); if (VA == 0 || Format == 0) { Gpu.Renderer.FrameBuffer.UnbindColor(FbIndex); return; } long Key = Vmm.GetPhysicalAddress(VA); FrameBuffers.Add(Key); int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10); int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10); float TX = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateX + FbIndex * 8); float TY = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateY + FbIndex * 8); float SX = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNScaleX + FbIndex * 8); float SY = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNScaleY + FbIndex * 8); int VpX = (int)MathF.Max(0, TX - MathF.Abs(SX)); int VpY = (int)MathF.Max(0, TY - MathF.Abs(SY)); int VpW = (int)(TX + MathF.Abs(SX)) - VpX; int VpH = (int)(TY + MathF.Abs(SY)) - VpY; GalImageFormat ImageFormat = ImageFormatConverter.ConvertFrameBuffer((GalFrameBufferFormat)Format); GalImage Image = new GalImage(Width, Height, ImageFormat); long Size = TextureHelper.GetTextureSize(Image); Gpu.Renderer.Texture.CreateFb(Key, Size, Image); Gpu.Renderer.FrameBuffer.BindColor(Key, FbIndex); Gpu.Renderer.FrameBuffer.SetViewport(VpX, VpY, VpW, VpH); }
private void TextureCopy(NvGpuVmm vmm) { CopyOperation operation = (CopyOperation)ReadRegister(NvGpuEngine2dReg.CopyOperation); int dstFormat = ReadRegister(NvGpuEngine2dReg.DstFormat); bool dstLinear = ReadRegister(NvGpuEngine2dReg.DstLinear) != 0; int dstWidth = ReadRegister(NvGpuEngine2dReg.DstWidth); int dstHeight = ReadRegister(NvGpuEngine2dReg.DstHeight); int dstDepth = ReadRegister(NvGpuEngine2dReg.DstDepth); int dstLayer = ReadRegister(NvGpuEngine2dReg.DstLayer); int dstPitch = ReadRegister(NvGpuEngine2dReg.DstPitch); int dstBlkDim = ReadRegister(NvGpuEngine2dReg.DstBlockDimensions); int srcFormat = ReadRegister(NvGpuEngine2dReg.SrcFormat); bool srcLinear = ReadRegister(NvGpuEngine2dReg.SrcLinear) != 0; int srcWidth = ReadRegister(NvGpuEngine2dReg.SrcWidth); int srcHeight = ReadRegister(NvGpuEngine2dReg.SrcHeight); int srcDepth = ReadRegister(NvGpuEngine2dReg.SrcDepth); int srcLayer = ReadRegister(NvGpuEngine2dReg.SrcLayer); int srcPitch = ReadRegister(NvGpuEngine2dReg.SrcPitch); int srcBlkDim = ReadRegister(NvGpuEngine2dReg.SrcBlockDimensions); int dstBlitX = ReadRegister(NvGpuEngine2dReg.BlitDstX); int dstBlitY = ReadRegister(NvGpuEngine2dReg.BlitDstY); int dstBlitW = ReadRegister(NvGpuEngine2dReg.BlitDstW); int dstBlitH = ReadRegister(NvGpuEngine2dReg.BlitDstH); long blitDuDx = ReadRegisterFixed1_31_32(NvGpuEngine2dReg.BlitDuDxFract); long blitDvDy = ReadRegisterFixed1_31_32(NvGpuEngine2dReg.BlitDvDyFract); long srcBlitX = ReadRegisterFixed1_31_32(NvGpuEngine2dReg.BlitSrcXFract); long srcBlitY = ReadRegisterFixed1_31_32(NvGpuEngine2dReg.BlitSrcYFract); GalImageFormat srcImgFormat = ImageUtils.ConvertSurface((GalSurfaceFormat)srcFormat); GalImageFormat dstImgFormat = ImageUtils.ConvertSurface((GalSurfaceFormat)dstFormat); GalMemoryLayout srcLayout = GetLayout(srcLinear); GalMemoryLayout dstLayout = GetLayout(dstLinear); int srcBlockHeight = 1 << ((srcBlkDim >> 4) & 0xf); int dstBlockHeight = 1 << ((dstBlkDim >> 4) & 0xf); long srcAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress); long dstAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.DstAddress); long srcKey = vmm.GetPhysicalAddress(srcAddress); long dstKey = vmm.GetPhysicalAddress(dstAddress); bool isSrcLayered = false; bool isDstLayered = false; GalTextureTarget srcTarget = GalTextureTarget.TwoD; if (srcDepth != 0) { srcTarget = GalTextureTarget.TwoDArray; srcDepth++; isSrcLayered = true; } else { srcDepth = 1; } GalTextureTarget dstTarget = GalTextureTarget.TwoD; if (dstDepth != 0) { dstTarget = GalTextureTarget.TwoDArray; dstDepth++; isDstLayered = true; } else { dstDepth = 1; } GalImage srcTexture = new GalImage( srcWidth, srcHeight, 1, srcDepth, 1, srcBlockHeight, 1, srcLayout, srcImgFormat, srcTarget); GalImage dstTexture = new GalImage( dstWidth, dstHeight, 1, dstDepth, 1, dstBlockHeight, 1, dstLayout, dstImgFormat, dstTarget); srcTexture.Pitch = srcPitch; dstTexture.Pitch = dstPitch; long GetLayerOffset(GalImage image, int layer) { int targetMipLevel = image.MaxMipmapLevel <= 1 ? 1 : image.MaxMipmapLevel - 1; return(ImageUtils.GetLayerOffset(image, targetMipLevel) * layer); } int srcLayerIndex = -1; if (isSrcLayered && _gpu.ResourceManager.TryGetTextureLayer(srcKey, out srcLayerIndex) && srcLayerIndex != 0) { srcKey = srcKey - GetLayerOffset(srcTexture, srcLayerIndex); } int dstLayerIndex = -1; if (isDstLayered && _gpu.ResourceManager.TryGetTextureLayer(dstKey, out dstLayerIndex) && dstLayerIndex != 0) { dstKey = dstKey - GetLayerOffset(dstTexture, dstLayerIndex); } _gpu.ResourceManager.SendTexture(vmm, srcKey, srcTexture); _gpu.ResourceManager.SendTexture(vmm, dstKey, dstTexture); if (isSrcLayered && srcLayerIndex == -1) { for (int layer = 0; layer < srcTexture.LayerCount; layer++) { _gpu.ResourceManager.SetTextureArrayLayer(srcKey + GetLayerOffset(srcTexture, layer), layer); } srcLayerIndex = 0; } if (isDstLayered && dstLayerIndex == -1) { for (int layer = 0; layer < dstTexture.LayerCount; layer++) { _gpu.ResourceManager.SetTextureArrayLayer(dstKey + GetLayerOffset(dstTexture, layer), layer); } dstLayerIndex = 0; } int srcBlitX1 = (int)(srcBlitX >> 32); int srcBlitY1 = (int)(srcBlitY >> 32); int srcBlitX2 = (int)(srcBlitX + dstBlitW * blitDuDx >> 32); int srcBlitY2 = (int)(srcBlitY + dstBlitH * blitDvDy >> 32); _gpu.Renderer.RenderTarget.Copy( srcTexture, dstTexture, srcKey, dstKey, srcLayerIndex, dstLayerIndex, srcBlitX1, srcBlitY1, srcBlitX2, srcBlitY2, dstBlitX, dstBlitY, dstBlitX + dstBlitW, dstBlitY + dstBlitH); //Do a guest side copy aswell. This is necessary when //the texture is modified by the guest, however it doesn't //work when resources that the gpu can write to are copied, //like framebuffers. // FIXME: SUPPORT MULTILAYER CORRECTLY HERE (this will cause weird stuffs on the first layer) ImageUtils.CopyTexture( vmm, srcTexture, dstTexture, srcAddress, dstAddress, srcBlitX1, srcBlitY1, dstBlitX, dstBlitY, dstBlitW, dstBlitH); vmm.IsRegionModified(dstKey, ImageUtils.GetSize(dstTexture), NvGpuBufferType.Texture); }
private void UploadVertexArrays(NvGpuVmm Vmm, GalPipelineState State) { long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); long IboKey = Vmm.GetPhysicalAddress(IndexPosition); int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount); GalIndexFormat IndexFormat = (GalIndexFormat)IndexEntryFmt; int IndexEntrySize = 1 << IndexEntryFmt; if (IndexEntrySize > 4) { throw new InvalidOperationException(); } if (IndexCount != 0) { int IbSize = IndexCount * IndexEntrySize; bool IboCached = Gpu.Renderer.Rasterizer.IsIboCached(IboKey, (uint)IbSize); if (!IboCached || QueryKeyUpload(Vmm, IboKey, (uint)IbSize, NvGpuBufferType.Index)) { IntPtr DataAddress = Vmm.GetHostAddress(IndexPosition, IbSize); Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, DataAddress); } Gpu.Renderer.Rasterizer.SetIndexArray(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)); } State.VertexBindings = new GalVertexBinding[32]; for (int Index = 0; Index < 32; Index++) { if (Attribs[Index] == null) { continue; } int Control = ReadRegister(NvGpuEngine3dReg.VertexArrayNControl + Index * 4); bool Enable = (Control & 0x1000) != 0; if (!Enable) { continue; } long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4); long VertexEndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 2); int VertexDivisor = ReadRegister(NvGpuEngine3dReg.VertexArrayNDivisor + Index * 4); bool Instanced = ReadRegisterBool(NvGpuEngine3dReg.VertexArrayNInstance + Index); int Stride = Control & 0xfff; if (Instanced && VertexDivisor != 0) { VertexPosition += Stride * (CurrentInstance / VertexDivisor); } if (VertexPosition > VertexEndPos) { //Instance is invalid, ignore the draw call continue; } long VboKey = Vmm.GetPhysicalAddress(VertexPosition); long VbSize = (VertexEndPos - VertexPosition) + 1; bool VboCached = Gpu.Renderer.Rasterizer.IsVboCached(VboKey, VbSize); if (!VboCached || QueryKeyUpload(Vmm, VboKey, VbSize, NvGpuBufferType.Vertex)) { IntPtr DataAddress = Vmm.GetHostAddress(VertexPosition, VbSize); Gpu.Renderer.Rasterizer.CreateVbo(VboKey, (int)VbSize, DataAddress); } State.VertexBindings[Index].Enabled = true; State.VertexBindings[Index].Stride = Stride; State.VertexBindings[Index].VboKey = VboKey; State.VertexBindings[Index].Instanced = Instanced; State.VertexBindings[Index].Divisor = VertexDivisor; State.VertexBindings[Index].Attribs = Attribs[Index].ToArray(); } }
private void UploadTexture(NvGpuVmm Vmm, int TexIndex, int TextureHandle) { if (TextureHandle == 0) { //TODO: Is this correct? //Some games like puyo puyo will have 0 handles. //It may be just normal behaviour or a bug caused by sync issues. //The game does initialize the value properly after through. return; } 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 Key = Vmm.ReadInt64(TicPosition + 4) & 0xffffffffffff; Key = Vmm.GetPhysicalAddress(Key); if (Key == -1) { //FIXME: Should'nt ignore invalid addresses. return; } if (IsFrameBufferPosition(Key)) { //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.FrameBuffer.BindTexture(Key, TexIndex); } else { GalImage NewImage = TextureFactory.MakeTexture(Vmm, TicPosition); long Size = (uint)TextureHelper.GetTextureSize(NewImage); bool HasCachedTexture = false; if (Gpu.Renderer.Texture.TryGetCachedTexture(Key, Size, out GalImage Image)) { if (NewImage.Equals(Image) && !QueryKeyUpload(Vmm, Key, Size, NvGpuBufferType.Texture)) { Gpu.Renderer.Texture.Bind(Key, TexIndex); HasCachedTexture = true; } } if (!HasCachedTexture) { byte[] Data = TextureFactory.GetTextureData(Vmm, TicPosition); Gpu.Renderer.Texture.Create(Key, Data, NewImage); } Gpu.Renderer.Texture.Bind(Key, TexIndex); } Gpu.Renderer.Texture.SetSampler(Sampler); }
private void UploadVertexArrays(NvGpuVmm Vmm, GalPipelineState State) { long IbPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); long IboKey = Vmm.GetPhysicalAddress(IbPosition); int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount); int PrimCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl); GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff); GalIndexFormat IndexFormat = (GalIndexFormat)IndexEntryFmt; int IndexEntrySize = 1 << IndexEntryFmt; if (IndexEntrySize > 4) { throw new InvalidOperationException("Invalid index entry size \"" + IndexEntrySize + "\"!"); } if (IndexCount != 0) { int IbSize = IndexCount * IndexEntrySize; bool IboCached = Gpu.Renderer.Rasterizer.IsIboCached(IboKey, (uint)IbSize); bool UsesLegacyQuads = PrimType == GalPrimitiveType.Quads || PrimType == GalPrimitiveType.QuadStrip; if (!IboCached || Gpu.ResourceManager.MemoryRegionModified(Vmm, IboKey, (uint)IbSize, NvGpuBufferType.Index)) { if (!UsesLegacyQuads) { if (Vmm.TryGetHostAddress(IbPosition, IbSize, out IntPtr IbPtr)) { Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, IbPtr); } else { Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, Vmm.ReadBytes(IbPosition, IbSize)); } } else { byte[] Buffer = Vmm.ReadBytes(IbPosition, IbSize); if (PrimType == GalPrimitiveType.Quads) { Buffer = QuadHelper.ConvertIbQuadsToTris(Buffer, IndexEntrySize, IndexCount); } else /* if (PrimType == GalPrimitiveType.QuadStrip) */ { Buffer = QuadHelper.ConvertIbQuadStripToTris(Buffer, IndexEntrySize, IndexCount); } Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, Buffer); } } if (!UsesLegacyQuads) { Gpu.Renderer.Rasterizer.SetIndexArray(IbSize, IndexFormat); } else { if (PrimType == GalPrimitiveType.Quads) { Gpu.Renderer.Rasterizer.SetIndexArray(QuadHelper.ConvertIbSizeQuadsToTris(IbSize), IndexFormat); } else /* if (PrimType == GalPrimitiveType.QuadStrip) */ { Gpu.Renderer.Rasterizer.SetIndexArray(QuadHelper.ConvertIbSizeQuadStripToTris(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>(); } long VbPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + ArrayIndex * 4); if (VbPosition == 0) { continue; } bool IsConst = ((Packed >> 6) & 1) != 0; int Offset = (Packed >> 7) & 0x3fff; GalVertexAttribSize Size = (GalVertexAttribSize)((Packed >> 21) & 0x3f); GalVertexAttribType Type = (GalVertexAttribType)((Packed >> 27) & 0x7); bool IsRgba = ((Packed >> 31) & 1) != 0; //Note: 16 is the maximum size of an attribute, //having a component size of 32-bits with 4 elements (a vec4). byte[] Data = Vmm.ReadBytes(VbPosition + Offset, 16); Attribs[ArrayIndex].Add(new GalVertexAttrib(Attr, IsConst, Offset, Data, Size, Type, IsRgba)); } State.VertexBindings = new GalVertexBinding[32]; for (int Index = 0; Index < 32; Index++) { if (Attribs[Index] == null) { continue; } int Control = ReadRegister(NvGpuEngine3dReg.VertexArrayNControl + Index * 4); bool Enable = (Control & 0x1000) != 0; if (!Enable) { continue; } long VbPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4); long VbEndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 2); int VertexDivisor = ReadRegister(NvGpuEngine3dReg.VertexArrayNDivisor + Index * 4); bool Instanced = ReadRegisterBool(NvGpuEngine3dReg.VertexArrayNInstance + Index); int Stride = Control & 0xfff; if (Instanced && VertexDivisor != 0) { VbPosition += Stride * (CurrentInstance / VertexDivisor); } if (VbPosition > VbEndPos) { //Instance is invalid, ignore the draw call continue; } long VboKey = Vmm.GetPhysicalAddress(VbPosition); long VbSize = (VbEndPos - VbPosition) + 1; bool VboCached = Gpu.Renderer.Rasterizer.IsVboCached(VboKey, VbSize); if (!VboCached || Gpu.ResourceManager.MemoryRegionModified(Vmm, VboKey, VbSize, NvGpuBufferType.Vertex)) { if (Vmm.TryGetHostAddress(VbPosition, VbSize, out IntPtr VbPtr)) { Gpu.Renderer.Rasterizer.CreateVbo(VboKey, (int)VbSize, VbPtr); } else { Gpu.Renderer.Rasterizer.CreateVbo(VboKey, Vmm.ReadBytes(VbPosition, VbSize)); } } State.VertexBindings[Index].Enabled = true; State.VertexBindings[Index].Stride = Stride; State.VertexBindings[Index].VboKey = VboKey; State.VertexBindings[Index].Instanced = Instanced; State.VertexBindings[Index].Divisor = VertexDivisor; State.VertexBindings[Index].Attribs = Attribs[Index].ToArray(); } }
private void TextureCopy(NvGpuVmm Vmm) { CopyOperation Operation = (CopyOperation)ReadRegister(NvGpuEngine2dReg.CopyOperation); int DstFormat = ReadRegister(NvGpuEngine2dReg.DstFormat); 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); int SrcFormat = ReadRegister(NvGpuEngine2dReg.SrcFormat); bool SrcLinear = ReadRegister(NvGpuEngine2dReg.SrcLinear) != 0; int SrcWidth = ReadRegister(NvGpuEngine2dReg.SrcWidth); int SrcHeight = ReadRegister(NvGpuEngine2dReg.SrcHeight); int SrcPitch = ReadRegister(NvGpuEngine2dReg.SrcPitch); int SrcBlkDim = ReadRegister(NvGpuEngine2dReg.SrcBlockDimensions); int DstBlitX = ReadRegister(NvGpuEngine2dReg.BlitDstX); int DstBlitY = ReadRegister(NvGpuEngine2dReg.BlitDstY); int DstBlitW = ReadRegister(NvGpuEngine2dReg.BlitDstW); int DstBlitH = ReadRegister(NvGpuEngine2dReg.BlitDstH); long BlitDuDx = ReadRegisterFixed1_31_32(NvGpuEngine2dReg.BlitDuDxFract); long BlitDvDy = ReadRegisterFixed1_31_32(NvGpuEngine2dReg.BlitDvDyFract); long SrcBlitX = ReadRegisterFixed1_31_32(NvGpuEngine2dReg.BlitSrcXFract); long SrcBlitY = ReadRegisterFixed1_31_32(NvGpuEngine2dReg.BlitSrcYFract); GalImageFormat SrcImgFormat = ImageUtils.ConvertSurface((GalSurfaceFormat)SrcFormat); GalImageFormat DstImgFormat = ImageUtils.ConvertSurface((GalSurfaceFormat)DstFormat); GalMemoryLayout SrcLayout = GetLayout(SrcLinear); GalMemoryLayout DstLayout = GetLayout(DstLinear); int SrcBlockHeight = 1 << ((SrcBlkDim >> 4) & 0xf); int DstBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf); long SrcAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress); long DstAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.DstAddress); long SrcKey = Vmm.GetPhysicalAddress(SrcAddress); long DstKey = Vmm.GetPhysicalAddress(DstAddress); GalImage SrcTexture = new GalImage( SrcWidth, SrcHeight, 1, SrcBlockHeight, SrcLayout, SrcImgFormat); GalImage DstTexture = new GalImage( DstWidth, DstHeight, 1, DstBlockHeight, DstLayout, DstImgFormat); SrcTexture.Pitch = SrcPitch; DstTexture.Pitch = DstPitch; Gpu.ResourceManager.SendTexture(Vmm, SrcKey, SrcTexture); Gpu.ResourceManager.SendTexture(Vmm, DstKey, DstTexture); int SrcBlitX1 = (int)(SrcBlitX >> 32); int SrcBlitY1 = (int)(SrcBlitY >> 32); int SrcBlitX2 = (int)(SrcBlitX + DstBlitW * BlitDuDx >> 32); int SrcBlitY2 = (int)(SrcBlitY + DstBlitH * BlitDvDy >> 32); Gpu.Renderer.RenderTarget.Copy( SrcKey, DstKey, SrcBlitX1, SrcBlitY1, SrcBlitX2, SrcBlitY2, DstBlitX, DstBlitY, DstBlitX + DstBlitW, DstBlitY + DstBlitH); //Do a guest side copy aswell. This is necessary when //the texture is modified by the guest, however it doesn't //work when resources that the gpu can write to are copied, //like framebuffers. ImageUtils.CopyTexture( Vmm, SrcTexture, DstTexture, SrcAddress, DstAddress, SrcBlitX1, SrcBlitY1, DstBlitX, DstBlitY, DstBlitW, DstBlitH); Vmm.IsRegionModified(DstKey, ImageUtils.GetSize(DstTexture), NvGpuBufferType.Texture); }
private void UploadVertexArrays(NvGpuVmm vmm, GalPipelineState state) { Profile.Begin(Profiles.GPU.Engine3d.UploadVertexArrays); long ibPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); long iboKey = vmm.GetPhysicalAddress(ibPosition); int indexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); int indexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount); int primCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl); GalPrimitiveType primType = (GalPrimitiveType)(primCtrl & 0xffff); GalIndexFormat indexFormat = (GalIndexFormat)indexEntryFmt; int indexEntrySize = 1 << indexEntryFmt; if (indexEntrySize > 4) { throw new InvalidOperationException("Invalid index entry size \"" + indexEntrySize + "\"!"); } if (indexCount != 0) { int ibSize = indexCount * indexEntrySize; bool iboCached = _gpu.Renderer.Rasterizer.IsIboCached(iboKey, (uint)ibSize); bool usesLegacyQuads = primType == GalPrimitiveType.Quads || primType == GalPrimitiveType.QuadStrip; if (!iboCached || _gpu.ResourceManager.MemoryRegionModified(vmm, iboKey, (uint)ibSize, NvGpuBufferType.Index)) { if (!usesLegacyQuads) { if (vmm.TryGetHostAddress(ibPosition, ibSize, out IntPtr ibPtr)) { _gpu.Renderer.Rasterizer.CreateIbo(iboKey, ibSize, ibPtr); } else { _gpu.Renderer.Rasterizer.CreateIbo(iboKey, ibSize, vmm.ReadBytes(ibPosition, ibSize)); } } else { byte[] buffer = vmm.ReadBytes(ibPosition, ibSize); if (primType == GalPrimitiveType.Quads) { buffer = QuadHelper.ConvertQuadsToTris(buffer, indexEntrySize, indexCount); } else /* if (PrimType == GalPrimitiveType.QuadStrip) */ { buffer = QuadHelper.ConvertQuadStripToTris(buffer, indexEntrySize, indexCount); } _gpu.Renderer.Rasterizer.CreateIbo(iboKey, ibSize, buffer); } } if (!usesLegacyQuads) { _gpu.Renderer.Rasterizer.SetIndexArray(ibSize, indexFormat); } else { if (primType == GalPrimitiveType.Quads) { _gpu.Renderer.Rasterizer.SetIndexArray(QuadHelper.ConvertSizeQuadsToTris(ibSize), indexFormat); } else /* if (PrimType == GalPrimitiveType.QuadStrip) */ { _gpu.Renderer.Rasterizer.SetIndexArray(QuadHelper.ConvertSizeQuadStripToTris(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>(); } long vbPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + arrayIndex * 4); if (vbPosition == 0) { continue; } bool isConst = ((packed >> 6) & 1) != 0; int offset = (packed >> 7) & 0x3fff; GalVertexAttribSize size = (GalVertexAttribSize)((packed >> 21) & 0x3f); GalVertexAttribType type = (GalVertexAttribType)((packed >> 27) & 0x7); bool isRgba = ((packed >> 31) & 1) != 0; // Check vertex array is enabled to avoid out of bounds exception when reading bytes bool enable = (ReadRegister(NvGpuEngine3dReg.VertexArrayNControl + arrayIndex * 4) & 0x1000) != 0; // Note: 16 is the maximum size of an attribute, // having a component size of 32-bits with 4 elements (a vec4). if (enable) { byte[] data = vmm.ReadBytes(vbPosition + offset, 16); attribs[arrayIndex].Add(new GalVertexAttrib(attr, isConst, offset, data, size, type, isRgba)); } } state.VertexBindings = new GalVertexBinding[32]; for (int index = 0; index < 32; index++) { if (attribs[index] == null) { continue; } int control = ReadRegister(NvGpuEngine3dReg.VertexArrayNControl + index * 4); bool enable = (control & 0x1000) != 0; if (!enable) { continue; } long vbPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + index * 4); long vbEndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + index * 2); int vertexDivisor = ReadRegister(NvGpuEngine3dReg.VertexArrayNDivisor + index * 4); bool instanced = ReadRegisterBool(NvGpuEngine3dReg.VertexArrayNInstance + index); int stride = control & 0xfff; if (instanced && vertexDivisor != 0) { vbPosition += stride * (_currentInstance / vertexDivisor); } if (vbPosition > vbEndPos) { // Instance is invalid, ignore the draw call continue; } long vboKey = vmm.GetPhysicalAddress(vbPosition); long vbSize = (vbEndPos - vbPosition) + 1; int modifiedVbSize = (int)vbSize; // If quads convert size to triangle length if (stride == 0) { if (primType == GalPrimitiveType.Quads) { modifiedVbSize = QuadHelper.ConvertSizeQuadsToTris(modifiedVbSize); } else if (primType == GalPrimitiveType.QuadStrip) { modifiedVbSize = QuadHelper.ConvertSizeQuadStripToTris(modifiedVbSize); } } bool vboCached = _gpu.Renderer.Rasterizer.IsVboCached(vboKey, modifiedVbSize); if (!vboCached || _gpu.ResourceManager.MemoryRegionModified(vmm, vboKey, vbSize, NvGpuBufferType.Vertex)) { if ((primType == GalPrimitiveType.Quads | primType == GalPrimitiveType.QuadStrip) && stride != 0) { // Convert quad buffer to triangles byte[] data = vmm.ReadBytes(vbPosition, vbSize); if (primType == GalPrimitiveType.Quads) { data = QuadHelper.ConvertQuadsToTris(data, stride, (int)(vbSize / stride)); } else { data = QuadHelper.ConvertQuadStripToTris(data, stride, (int)(vbSize / stride)); } _gpu.Renderer.Rasterizer.CreateVbo(vboKey, data); } else if (vmm.TryGetHostAddress(vbPosition, vbSize, out IntPtr vbPtr)) { _gpu.Renderer.Rasterizer.CreateVbo(vboKey, (int)vbSize, vbPtr); } else { _gpu.Renderer.Rasterizer.CreateVbo(vboKey, vmm.ReadBytes(vbPosition, vbSize)); } } state.VertexBindings[index].Enabled = true; state.VertexBindings[index].Stride = stride; state.VertexBindings[index].VboKey = vboKey; state.VertexBindings[index].Instanced = instanced; state.VertexBindings[index].Divisor = vertexDivisor; state.VertexBindings[index].Attribs = attribs[index].ToArray(); } Profile.End(Profiles.GPU.Engine3d.UploadVertexArrays); }
private void Execute(NvGpuVmm vmm, GpuMethodCall methCall) { Profile.Begin(Profiles.GPU.EngineM2mf.Execute); //TODO: Some registers and copy modes are still not implemented. int control = methCall.Argument; bool srcLinear = ((control >> 7) & 1) != 0; bool dstLinear = ((control >> 8) & 1) != 0; bool copy2D = ((control >> 9) & 1) != 0; long srcAddress = MakeInt64From2xInt32(NvGpuEngineM2mfReg.SrcAddress); long dstAddress = MakeInt64From2xInt32(NvGpuEngineM2mfReg.DstAddress); int srcPitch = ReadRegister(NvGpuEngineM2mfReg.SrcPitch); int dstPitch = ReadRegister(NvGpuEngineM2mfReg.DstPitch); int xCount = ReadRegister(NvGpuEngineM2mfReg.XCount); int yCount = ReadRegister(NvGpuEngineM2mfReg.YCount); int swizzle = ReadRegister(NvGpuEngineM2mfReg.Swizzle); int dstBlkDim = ReadRegister(NvGpuEngineM2mfReg.DstBlkDim); int dstSizeX = ReadRegister(NvGpuEngineM2mfReg.DstSizeX); int dstSizeY = ReadRegister(NvGpuEngineM2mfReg.DstSizeY); int dstSizeZ = ReadRegister(NvGpuEngineM2mfReg.DstSizeZ); int dstPosXY = ReadRegister(NvGpuEngineM2mfReg.DstPosXY); int dstPosZ = ReadRegister(NvGpuEngineM2mfReg.DstPosZ); int srcBlkDim = ReadRegister(NvGpuEngineM2mfReg.SrcBlkDim); int srcSizeX = ReadRegister(NvGpuEngineM2mfReg.SrcSizeX); int srcSizeY = ReadRegister(NvGpuEngineM2mfReg.SrcSizeY); int srcSizeZ = ReadRegister(NvGpuEngineM2mfReg.SrcSizeZ); int srcPosXY = ReadRegister(NvGpuEngineM2mfReg.SrcPosXY); int srcPosZ = ReadRegister(NvGpuEngineM2mfReg.SrcPosZ); int srcCpp = ((swizzle >> 20) & 7) + 1; int dstCpp = ((swizzle >> 24) & 7) + 1; 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); long srcPa = vmm.GetPhysicalAddress(srcAddress); long dstPa = vmm.GetPhysicalAddress(dstAddress); if (copy2D) { if (srcLinear) { srcPosX = srcPosY = srcPosZ = 0; } if (dstLinear) { dstPosX = dstPosY = dstPosZ = 0; } if (srcLinear && dstLinear) { for (int y = 0; y < yCount; y++) { int srcOffset = (srcPosY + y) * srcPitch + srcPosX * srcCpp; int dstOffset = (dstPosY + y) * dstPitch + dstPosX * dstCpp; long src = srcPa + (uint)srcOffset; long dst = dstPa + (uint)dstOffset; vmm.Memory.CopyBytes(src, dst, xCount * srcCpp); } } else { ISwizzle srcSwizzle; if (srcLinear) { srcSwizzle = new LinearSwizzle(srcPitch, srcCpp, srcSizeX, srcSizeY); } else { srcSwizzle = new BlockLinearSwizzle( srcSizeX, srcSizeY, 1, srcBlockHeight, 1, srcCpp); } ISwizzle dstSwizzle; if (dstLinear) { dstSwizzle = new LinearSwizzle(dstPitch, dstCpp, srcSizeX, srcSizeY); } else { dstSwizzle = new BlockLinearSwizzle( dstSizeX, dstSizeY, 1, dstBlockHeight, 1, dstCpp); } // Calculate the bits per pixel int bpp = srcPitch / xCount; // Copying all the bits at the same time corrupts the texture, unknown why but probably because the texture isn't linear // To avoid this we will simply loop more times to cover all the bits, // this allows up to recalculate the memory locations for each iteration around the loop xCount *= bpp / srcCpp; for (int y = 0; y < yCount; y++) { for (int x = 0; x < xCount; x++) { int srcOffset = srcSwizzle.GetSwizzleOffset(srcPosX + x, srcPosY + y, 0); int dstOffset = dstSwizzle.GetSwizzleOffset(dstPosX + x, dstPosY + y, 0); long src = srcPa + (uint)srcOffset; long dst = dstPa + (uint)dstOffset; vmm.Memory.CopyBytes(src, dst, srcCpp); } } } } else { vmm.Memory.CopyBytes(srcPa, dstPa, xCount); } Profile.End(Profiles.GPU.EngineM2mf.Execute); }
private void DispatchRender(NvGpuVmm vmm, GalPipelineState state) { int indexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount); int primCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl); GalPrimitiveType primType = (GalPrimitiveType)(primCtrl & 0xffff); bool instanceNext = ((primCtrl >> 26) & 1) != 0; bool instanceCont = ((primCtrl >> 27) & 1) != 0; if (instanceNext && instanceCont) { throw new InvalidOperationException("GPU tried to increase and reset instance count at the same time"); } if (instanceNext) { _currentInstance++; } else if (!instanceCont) { _currentInstance = 0; } state.Instance = _currentInstance; _gpu.Renderer.Pipeline.Bind(state); _gpu.Renderer.RenderTarget.Bind(); if (indexCount != 0) { int indexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); int indexFirst = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst); int vertexBase = ReadRegister(NvGpuEngine3dReg.VertexArrayElemBase); long indexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); long iboKey = vmm.GetPhysicalAddress(indexPosition); // Quad primitive types were deprecated on OpenGL 3.x, // they are converted to a triangles index buffer on IB creation, // so we should use the triangles type here too. if (primType == GalPrimitiveType.Quads || primType == GalPrimitiveType.QuadStrip) { // Note: We assume that index first points to the first // vertex of a quad, if it points to the middle of a // quad (First % 4 != 0 for Quads) then it will not work properly. if (primType == GalPrimitiveType.Quads) { indexFirst = QuadHelper.ConvertSizeQuadsToTris(indexFirst); } else // QuadStrip { indexFirst = QuadHelper.ConvertSizeQuadStripToTris(indexFirst); } primType = GalPrimitiveType.Triangles; } _gpu.Renderer.Rasterizer.DrawElements(iboKey, indexFirst, vertexBase, primType); } else { int vertexFirst = ReadRegister(NvGpuEngine3dReg.VertexArrayFirst); int vertexCount = ReadRegister(NvGpuEngine3dReg.VertexArrayCount); // Quad primitive types were deprecated on OpenGL 3.x, // they are converted to a triangles index buffer on IB creation, // so we should use the triangles type here too. if (primType == GalPrimitiveType.Quads || primType == GalPrimitiveType.QuadStrip) { // Note: We assume that index first points to the first // vertex of a quad, if it points to the middle of a // quad (First % 4 != 0 for Quads) then it will not work properly. if (primType == GalPrimitiveType.Quads) { vertexFirst = QuadHelper.ConvertSizeQuadsToTris(vertexFirst); } else // QuadStrip { vertexFirst = QuadHelper.ConvertSizeQuadStripToTris(vertexFirst); } primType = GalPrimitiveType.Triangles; vertexCount = QuadHelper.ConvertSizeQuadsToTris(vertexCount); } _gpu.Renderer.Rasterizer.DrawArrays(vertexFirst, vertexCount, primType); } // Reset pipeline for host OpenGL calls _gpu.Renderer.Pipeline.Unbind(state); // Is the GPU really clearing those registers after draw? WriteRegister(NvGpuEngine3dReg.IndexBatchFirst, 0); WriteRegister(NvGpuEngine3dReg.IndexBatchCount, 0); }