Exemple #1
0
        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;
            }
        }
Exemple #2
0
        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;
            }
        }
Exemple #3
0
        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);
        }
Exemple #4
0
        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;
                }
            }
        }
Exemple #5
0
        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);
        }
Exemple #6
0
        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;
                }
            }
        }
Exemple #7
0
        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);
        }
Exemple #8
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);
        }
Exemple #9
0
        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);
        }
Exemple #10
0
        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);
        }
Exemple #11
0
        private byte[] ReadShaderBinary(NvGpuVmm Vmm, long Key)
        {
            long Size = GetShaderSize(Vmm, Key);

            long Address = Vmm.GetPhysicalAddress(Key);

            return(Vmm.ReadBytes(Key, Size));
        }
Exemple #12
0
        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);
        }
Exemple #13
0
        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);
        }
Exemple #14
0
        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);
        }
Exemple #15
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);
        }
Exemple #16
0
        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);
        }
Exemple #17
0
        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);
        }
Exemple #18
0
        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);
        }
Exemple #19
0
        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);
        }
Exemple #20
0
        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);
        }
Exemple #21
0
        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);
        }
Exemple #22
0
        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);
        }
Exemple #23
0
        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);
        }
Exemple #24
0
        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();
            }
        }
Exemple #25
0
        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);
        }
Exemple #26
0
        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();
            }
        }
Exemple #27
0
        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);
        }
Exemple #28
0
        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);
        }
Exemple #29
0
        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);
        }
Exemple #30
0
        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);
        }