Esempio n. 1
0
        /// <summary>
        /// Finishes the draw call.
        /// This draws geometry on the bound buffers based on the current GPU state.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        /// <param name="argument">Method call argument</param>
        private void DrawEnd(GpuState state, int argument)
        {
            var indexBuffer = state.Get <IndexBufferState>(MethodOffset.IndexBufferState);

            DrawEnd(state, indexBuffer.First, indexBuffer.Count);
        }
Esempio n. 2
0
        /// <summary>
        /// Finishes draw call.
        /// This draws geometry on the bound buffers based on the current GPU state.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        /// <param name="argument">Method call argument</param>
        private void DrawEnd(GpuState state, int argument)
        {
            ConditionalRenderEnabled renderEnable = GetRenderEnable(state);

            if (renderEnable == ConditionalRenderEnabled.False || _instancedDrawPending)
            {
                if (renderEnable == ConditionalRenderEnabled.False)
                {
                    PerformDeferredDraws();
                }

                _drawIndexed = false;

                if (renderEnable == ConditionalRenderEnabled.Host)
                {
                    _context.Renderer.Pipeline.EndHostConditionalRendering();
                }

                return;
            }

            UpdateState(state);

            bool instanced = _vsUsesInstanceId || _isAnyVbInstanced;

            if (instanced)
            {
                _instancedDrawPending = true;

                _instancedIndexed = _drawIndexed;

                _instancedFirstIndex    = _firstIndex;
                _instancedFirstVertex   = state.Get <int>(MethodOffset.FirstVertex);
                _instancedFirstInstance = state.Get <int>(MethodOffset.FirstInstance);

                _instancedIndexCount = _indexCount;

                var drawState = state.Get <VertexBufferDrawState>(MethodOffset.VertexBufferDrawState);

                _instancedDrawStateFirst = drawState.First;
                _instancedDrawStateCount = drawState.Count;

                _drawIndexed = false;

                if (renderEnable == ConditionalRenderEnabled.Host)
                {
                    _context.Renderer.Pipeline.EndHostConditionalRendering();
                }

                return;
            }

            int firstInstance = state.Get <int>(MethodOffset.FirstInstance);

            if (_drawIndexed)
            {
                _drawIndexed = false;

                int firstVertex = state.Get <int>(MethodOffset.FirstVertex);

                _context.Renderer.Pipeline.DrawIndexed(
                    _indexCount,
                    1,
                    _firstIndex,
                    firstVertex,
                    firstInstance);
            }
            else
            {
                var drawState = state.Get <VertexBufferDrawState>(MethodOffset.VertexBufferDrawState);

                _context.Renderer.Pipeline.Draw(
                    drawState.Count,
                    1,
                    drawState.First,
                    firstInstance);
            }

            if (renderEnable == ConditionalRenderEnabled.Host)
            {
                _context.Renderer.Pipeline.EndHostConditionalRendering();
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Clears the current color and depth-stencil buffers.
        /// Which buffers should be cleared is also specified on the argument.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        /// <param name="argument">Method call argument</param>
        private void Clear(GpuState state, int argument)
        {
            ConditionalRenderEnabled renderEnable = GetRenderEnable(state);

            if (renderEnable == ConditionalRenderEnabled.False)
            {
                return;
            }

            // Scissor and rasterizer discard also affect clears.
            if (state.QueryModified(MethodOffset.ScissorState))
            {
                UpdateScissorState(state);
            }

            if (state.QueryModified(MethodOffset.RasterizeEnable))
            {
                UpdateRasterizerState(state);
            }

            int index = (argument >> 6) & 0xf;

            UpdateRenderTargetState(state, useControl: false, singleUse: index);

            state.Channel.TextureManager.UpdateRenderTargets();

            bool clearDepth   = (argument & 1) != 0;
            bool clearStencil = (argument & 2) != 0;

            uint componentMask = (uint)((argument >> 2) & 0xf);

            if (componentMask != 0)
            {
                var clearColor = state.Get <ClearColors>(MethodOffset.ClearColors);

                ColorF color = new ColorF(
                    clearColor.Red,
                    clearColor.Green,
                    clearColor.Blue,
                    clearColor.Alpha);

                _context.Renderer.Pipeline.ClearRenderTargetColor(index, componentMask, color);
            }

            if (clearDepth || clearStencil)
            {
                float depthValue   = state.Get <float>(MethodOffset.ClearDepthValue);
                int   stencilValue = state.Get <int>  (MethodOffset.ClearStencilValue);

                int stencilMask = 0;

                if (clearStencil)
                {
                    stencilMask = state.Get <StencilTestState>(MethodOffset.StencilTestState).FrontMask;
                }

                _context.Renderer.Pipeline.ClearRenderTargetDepthStencil(
                    depthValue,
                    clearDepth,
                    stencilValue,
                    stencilMask);
            }

            UpdateRenderTargetState(state, useControl: true);

            if (renderEnable == ConditionalRenderEnabled.Host)
            {
                _context.Renderer.Pipeline.EndHostConditionalRendering();
            }
        }
Esempio n. 4
0
        /// <summary>
        /// Updates Rasterizer primitive discard state based on guest gpu state.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        private void UpdateRasterizerState(GpuState state)
        {
            Boolean32 enable = state.Get <Boolean32>(MethodOffset.RasterizeEnable);

            _context.Renderer.Pipeline.SetRasterizerDiscard(!enable);
        }
Esempio n. 5
0
        /// <summary>
        /// Performs a buffer to buffer, or buffer to texture copy.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        /// <param name="argument">Method call argument</param>
        private void CopyBuffer(GpuState state, int argument)
        {
            var cbp = state.Get <CopyBufferParams>(MethodOffset.CopyBufferParams);

            var swizzle = state.Get <CopyBufferSwizzle>(MethodOffset.CopyBufferSwizzle);

            bool srcLinear = (argument & (1 << 7)) != 0;
            bool dstLinear = (argument & (1 << 8)) != 0;
            bool copy2D    = (argument & (1 << 9)) != 0;

            int size = cbp.XCount;

            if (size == 0)
            {
                return;
            }

            if (copy2D)
            {
                // Buffer to texture copy.
                int srcBpp = swizzle.UnpackSrcComponentsCount() * swizzle.UnpackComponentSize();
                int dstBpp = swizzle.UnpackDstComponentsCount() * swizzle.UnpackComponentSize();

                var dst = state.Get <CopyBufferTexture>(MethodOffset.CopyBufferDstTexture);
                var src = state.Get <CopyBufferTexture>(MethodOffset.CopyBufferSrcTexture);

                var srcCalculator = new OffsetCalculator(
                    src.Width,
                    src.Height,
                    cbp.SrcStride,
                    srcLinear,
                    src.MemoryLayout.UnpackGobBlocksInY(),
                    srcBpp);

                var dstCalculator = new OffsetCalculator(
                    dst.Width,
                    dst.Height,
                    cbp.DstStride,
                    dstLinear,
                    dst.MemoryLayout.UnpackGobBlocksInY(),
                    dstBpp);

                ulong srcBaseAddress = _context.MemoryManager.Translate(cbp.SrcAddress.Pack());
                ulong dstBaseAddress = _context.MemoryManager.Translate(cbp.DstAddress.Pack());

                for (int y = 0; y < cbp.YCount; y++)
                {
                    for (int x = 0; x < cbp.XCount; x++)
                    {
                        int srcOffset = srcCalculator.GetOffset(src.RegionX + x, src.RegionY + y);
                        int dstOffset = dstCalculator.GetOffset(dst.RegionX + x, dst.RegionY + y);

                        ulong srcAddress = srcBaseAddress + (ulong)srcOffset;
                        ulong dstAddress = dstBaseAddress + (ulong)dstOffset;

                        ReadOnlySpan <byte> pixel = _context.PhysicalMemory.GetSpan(srcAddress, srcBpp);

                        _context.PhysicalMemory.Write(dstAddress, pixel);
                    }
                }
            }
            else
            {
                // Buffer to buffer copy.
                BufferManager.CopyBuffer(cbp.SrcAddress, cbp.DstAddress, (uint)size);
            }
        }
Esempio n. 6
0
        /// <summary>
        /// Updates host point size based on guest GPU state.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        private void UpdatePointSizeState(GpuState state)
        {
            float size = state.Get <float>(MethodOffset.PointSize);

            _context.Renderer.Pipeline.SetPointSize(size);
        }
Esempio n. 7
0
        /// <summary>
        /// Performs a buffer to buffer, or buffer to texture copy.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        /// <param name="argument">Method call argument</param>
        private void CopyBuffer(GpuState state, int argument)
        {
            var cbp = state.Get <CopyBufferParams>(MethodOffset.CopyBufferParams);

            var swizzle = state.Get <CopyBufferSwizzle>(MethodOffset.CopyBufferSwizzle);

            CopyFlags copyFlags = (CopyFlags)argument;

            bool srcLinear = copyFlags.HasFlag(CopyFlags.SrcLinear);
            bool dstLinear = copyFlags.HasFlag(CopyFlags.DstLinear);
            bool copy2D    = copyFlags.HasFlag(CopyFlags.MultiLineEnable);
            bool remap     = copyFlags.HasFlag(CopyFlags.RemapEnable);

            int size = cbp.XCount;

            if (size == 0)
            {
                return;
            }

            FlushUboUpdate();

            if (copy2D)
            {
                // Buffer to texture copy.
                int srcBpp = remap ? swizzle.UnpackSrcComponentsCount() * swizzle.UnpackComponentSize() : 1;
                int dstBpp = remap ? swizzle.UnpackDstComponentsCount() * swizzle.UnpackComponentSize() : 1;

                var dst = state.Get <CopyBufferTexture>(MethodOffset.CopyBufferDstTexture);
                var src = state.Get <CopyBufferTexture>(MethodOffset.CopyBufferSrcTexture);

                var srcCalculator = new OffsetCalculator(
                    src.Width,
                    src.Height,
                    cbp.SrcStride,
                    srcLinear,
                    src.MemoryLayout.UnpackGobBlocksInY(),
                    src.MemoryLayout.UnpackGobBlocksInZ(),
                    srcBpp);

                var dstCalculator = new OffsetCalculator(
                    dst.Width,
                    dst.Height,
                    cbp.DstStride,
                    dstLinear,
                    dst.MemoryLayout.UnpackGobBlocksInY(),
                    dst.MemoryLayout.UnpackGobBlocksInZ(),
                    dstBpp);

                ulong srcBaseAddress = _context.MemoryManager.Translate(cbp.SrcAddress.Pack());
                ulong dstBaseAddress = _context.MemoryManager.Translate(cbp.DstAddress.Pack());

                (int srcBaseOffset, int srcSize) = srcCalculator.GetRectangleRange(src.RegionX, src.RegionY, cbp.XCount, cbp.YCount);
                (int dstBaseOffset, int dstSize) = dstCalculator.GetRectangleRange(dst.RegionX, dst.RegionY, cbp.XCount, cbp.YCount);

                ReadOnlySpan <byte> srcSpan = _context.PhysicalMemory.GetSpan(srcBaseAddress + (ulong)srcBaseOffset, srcSize, true);
                Span <byte>         dstSpan = _context.PhysicalMemory.GetSpan(dstBaseAddress + (ulong)dstBaseOffset, dstSize).ToArray();

                bool completeSource = IsTextureCopyComplete(cbp, src, srcLinear, srcBpp, cbp.SrcStride);
                bool completeDest   = IsTextureCopyComplete(cbp, dst, dstLinear, dstBpp, cbp.DstStride);

                if (completeSource && completeDest)
                {
                    Image.Texture target = TextureManager.FindTexture(dst, cbp, swizzle, dstLinear);
                    if (target != null)
                    {
                        ReadOnlySpan <byte> data;
                        if (srcLinear)
                        {
                            data = LayoutConverter.ConvertLinearStridedToLinear(
                                target.Info.Width,
                                target.Info.Height,
                                1,
                                1,
                                cbp.SrcStride,
                                target.Info.FormatInfo.BytesPerPixel,
                                srcSpan);
                        }
                        else
                        {
                            data = LayoutConverter.ConvertBlockLinearToLinear(
                                src.Width,
                                src.Height,
                                1,
                                target.Info.Levels,
                                1,
                                1,
                                1,
                                srcBpp,
                                src.MemoryLayout.UnpackGobBlocksInY(),
                                src.MemoryLayout.UnpackGobBlocksInZ(),
                                1,
                                new SizeInfo((int)target.Size),
                                srcSpan);
                        }

                        target.SetData(data);
                        target.SignalModified();

                        return;
                    }
                    else if (srcCalculator.LayoutMatches(dstCalculator))
                    {
                        srcSpan.CopyTo(dstSpan); // No layout conversion has to be performed, just copy the data entirely.

                        _context.PhysicalMemory.Write(dstBaseAddress + (ulong)dstBaseOffset, dstSpan);

                        return;
                    }
                }

                unsafe bool Convert <T>(Span <byte> dstSpan, ReadOnlySpan <byte> srcSpan) where T : unmanaged
                {
                    fixed(byte *dstPtr = dstSpan, srcPtr = srcSpan)
                    {
                        byte *dstBase = dstPtr - dstBaseOffset; // Layout offset is relative to the base, so we need to subtract the span's offset.
                        byte *srcBase = srcPtr - srcBaseOffset;

                        for (int y = 0; y < cbp.YCount; y++)
                        {
                            srcCalculator.SetY(src.RegionY + y);
                            dstCalculator.SetY(dst.RegionY + y);

                            for (int x = 0; x < cbp.XCount; x++)
                            {
                                int srcOffset = srcCalculator.GetOffset(src.RegionX + x);
                                int dstOffset = dstCalculator.GetOffset(dst.RegionX + x);

                                *(T *)(dstBase + dstOffset) = *(T *)(srcBase + srcOffset);
                            }
                        }
                    }

                    return(true);
                }

                bool _ = srcBpp switch
                {
                    1 => Convert <byte>(dstSpan, srcSpan),
                    2 => Convert <ushort>(dstSpan, srcSpan),
                    4 => Convert <uint>(dstSpan, srcSpan),
                    8 => Convert <ulong>(dstSpan, srcSpan),
                    12 => Convert <Bpp12Pixel>(dstSpan, srcSpan),
                    16 => Convert <Vector128 <byte> >(dstSpan, srcSpan),
                    _ => throw new NotSupportedException($"Unable to copy ${srcBpp} bpp pixel format.")
                };

                _context.PhysicalMemory.Write(dstBaseAddress + (ulong)dstBaseOffset, dstSpan);
            }
            else
            {
                if (remap &&
                    swizzle.UnpackDstX() == BufferSwizzleComponent.ConstA &&
                    swizzle.UnpackDstY() == BufferSwizzleComponent.ConstA &&
                    swizzle.UnpackDstZ() == BufferSwizzleComponent.ConstA &&
                    swizzle.UnpackDstW() == BufferSwizzleComponent.ConstA &&
                    swizzle.UnpackSrcComponentsCount() == 1 &&
                    swizzle.UnpackDstComponentsCount() == 1 &&
                    swizzle.UnpackComponentSize() == 4)
                {
                    // Fast path for clears when remap is enabled.
                    BufferManager.ClearBuffer(cbp.DstAddress, (uint)size * 4, state.Get <uint>(MethodOffset.CopyBufferConstA));
                }
                else
                {
                    // TODO: Implement remap functionality.
                    // Buffer to buffer copy.
                    BufferManager.CopyBuffer(cbp.SrcAddress, cbp.DstAddress, (uint)size);
                }
            }
        }
Esempio n. 8
0
        /// <summary>
        /// Performs a texture to texture copy.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        /// <param name="argument">Method call argument</param>
        private void CopyTexture(GpuState state, int argument)
        {
            var dstCopyTexture = state.Get <CopyTexture>(MethodOffset.CopyDstTexture);
            var srcCopyTexture = state.Get <CopyTexture>(MethodOffset.CopySrcTexture);

            Texture srcTexture = TextureManager.FindOrCreateTexture(srcCopyTexture);

            if (srcTexture == null)
            {
                return;
            }

            // When the source texture that was found has a depth format,
            // we must enforce the target texture also has a depth format,
            // as copies between depth and color formats are not allowed.
            if (srcTexture.Format == Format.D32Float)
            {
                dstCopyTexture.Format = RtFormat.D32Float;
            }

            Texture dstTexture = TextureManager.FindOrCreateTexture(dstCopyTexture);

            if (dstTexture == null)
            {
                return;
            }

            var control = state.Get <CopyTextureControl>(MethodOffset.CopyTextureControl);

            var region = state.Get <CopyRegion>(MethodOffset.CopyRegion);

            int srcX1 = (int)(region.SrcXF >> 32);
            int srcY1 = (int)(region.SrcYF >> 32);

            int srcX2 = (int)((region.SrcXF + region.SrcWidthRF * region.DstWidth) >> 32);
            int srcY2 = (int)((region.SrcYF + region.SrcHeightRF * region.DstHeight) >> 32);

            int dstX1 = region.DstX;
            int dstY1 = region.DstY;

            int dstX2 = region.DstX + region.DstWidth;
            int dstY2 = region.DstY + region.DstHeight;

            Extents2D srcRegion = new Extents2D(
                srcX1 / srcTexture.Info.SamplesInX,
                srcY1 / srcTexture.Info.SamplesInY,
                srcX2 / srcTexture.Info.SamplesInX,
                srcY2 / srcTexture.Info.SamplesInY);

            Extents2D dstRegion = new Extents2D(
                dstX1 / dstTexture.Info.SamplesInX,
                dstY1 / dstTexture.Info.SamplesInY,
                dstX2 / dstTexture.Info.SamplesInX,
                dstY2 / dstTexture.Info.SamplesInY);

            bool linearFilter = control.UnpackLinearFilter();

            srcTexture.HostTexture.CopyTo(dstTexture.HostTexture, srcRegion, dstRegion, linearFilter);

            // For an out of bounds copy, we must ensure that the copy wraps to the next line,
            // so for a copy from a 64x64 texture, in the region [32, 96[, there are 32 pixels that are
            // outside the bounds of the texture. We fill the destination with the first 32 pixels
            // of the next line on the source texture.
            // This can be emulated with 2 copies (the first copy handles the region inside the bounds,
            // the second handles the region outside of the bounds).
            // We must also extend the source texture by one line to ensure we can wrap on the last line.
            // This is required by the (guest) OpenGL driver.
            if (srcRegion.X2 > srcTexture.Info.Width)
            {
                srcCopyTexture.Height++;

                srcTexture = TextureManager.FindOrCreateTexture(srcCopyTexture);

                srcRegion = new Extents2D(
                    srcRegion.X1 - srcTexture.Info.Width,
                    srcRegion.Y1 + 1,
                    srcRegion.X2 - srcTexture.Info.Width,
                    srcRegion.Y2 + 1);

                srcTexture.HostTexture.CopyTo(dstTexture.HostTexture, srcRegion, dstRegion, linearFilter);
            }

            dstTexture.SignalModified();
        }
Esempio n. 9
0
        /// <summary>
        /// Updates render targets (color and depth-stencil buffers) based on current render target state.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        /// <param name="useControl">Use draw buffers information from render target control register</param>
        /// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
        private void UpdateRenderTargetState(GpuState state, bool useControl, int singleUse = -1)
        {
            var memoryManager = state.Channel.MemoryManager;
            var rtControl     = state.Get <RtControl>(MethodOffset.RtControl);

            int count = useControl ? rtControl.UnpackCount() : Constants.TotalRenderTargets;

            var msaaMode = state.Get <TextureMsaaMode>(MethodOffset.RtMsaaMode);

            int samplesInX = msaaMode.SamplesInX();
            int samplesInY = msaaMode.SamplesInY();

            var  scissor  = state.Get <ScreenScissorState>(MethodOffset.ScreenScissorState);
            Size sizeHint = new Size(scissor.X + scissor.Width, scissor.Y + scissor.Height, 1);

            bool changedScale = false;

            for (int index = 0; index < Constants.TotalRenderTargets; index++)
            {
                int rtIndex = useControl ? rtControl.UnpackPermutationIndex(index) : index;

                var colorState = state.Get <RtColorState>(MethodOffset.RtColorState, rtIndex);

                if (index >= count || !IsRtEnabled(colorState))
                {
                    changedScale |= state.Channel.TextureManager.SetRenderTargetColor(index, null);

                    continue;
                }

                Texture color = memoryManager.Physical.TextureCache.FindOrCreateTexture(
                    memoryManager,
                    colorState,
                    samplesInX,
                    samplesInY,
                    sizeHint);

                changedScale |= state.Channel.TextureManager.SetRenderTargetColor(index, color);
            }

            bool dsEnable = state.Get <Boolean32>(MethodOffset.RtDepthStencilEnable);

            Texture depthStencil = null;

            if (dsEnable)
            {
                var dsState = state.Get <RtDepthStencilState>(MethodOffset.RtDepthStencilState);
                var dsSize  = state.Get <Size3D>(MethodOffset.RtDepthStencilSize);

                depthStencil = memoryManager.Physical.TextureCache.FindOrCreateTexture(
                    memoryManager,
                    dsState,
                    dsSize,
                    samplesInX,
                    samplesInY,
                    sizeHint);
            }

            changedScale |= state.Channel.TextureManager.SetRenderTargetDepthStencil(depthStencil);

            if (changedScale)
            {
                state.Channel.TextureManager.UpdateRenderTargetScale(singleUse);
                _context.Renderer.Pipeline.SetRenderTargetScale(state.Channel.TextureManager.RenderTargetScale);

                UpdateViewportTransform(state);
                UpdateScissorState(state);
            }
        }
Esempio n. 10
0
        /// <summary>
        /// Performs a buffer to buffer, or buffer to texture copy.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        /// <param name="argument">Method call argument</param>
        private void CopyBuffer(GpuState state, int argument)
        {
            var cbp = state.Get <CopyBufferParams>(MethodOffset.CopyBufferParams);

            var swizzle = state.Get <CopyBufferSwizzle>(MethodOffset.CopyBufferSwizzle);

            bool srcLinear = (argument & (1 << 7)) != 0;
            bool dstLinear = (argument & (1 << 8)) != 0;
            bool copy2D    = (argument & (1 << 9)) != 0;

            int size = cbp.XCount;

            if (size == 0)
            {
                return;
            }

            if (copy2D)
            {
                // Buffer to texture copy.
                int srcBpp = swizzle.UnpackSrcComponentsCount() * swizzle.UnpackComponentSize();
                int dstBpp = swizzle.UnpackDstComponentsCount() * swizzle.UnpackComponentSize();

                var dst = state.Get <CopyBufferTexture>(MethodOffset.CopyBufferDstTexture);
                var src = state.Get <CopyBufferTexture>(MethodOffset.CopyBufferSrcTexture);

                var srcCalculator = new OffsetCalculator(
                    src.Width,
                    src.Height,
                    cbp.SrcStride,
                    srcLinear,
                    src.MemoryLayout.UnpackGobBlocksInY(),
                    src.MemoryLayout.UnpackGobBlocksInZ(),
                    srcBpp);

                var dstCalculator = new OffsetCalculator(
                    dst.Width,
                    dst.Height,
                    cbp.DstStride,
                    dstLinear,
                    dst.MemoryLayout.UnpackGobBlocksInY(),
                    dst.MemoryLayout.UnpackGobBlocksInZ(),
                    dstBpp);

                ulong srcBaseAddress = _context.MemoryManager.Translate(cbp.SrcAddress.Pack());
                ulong dstBaseAddress = _context.MemoryManager.Translate(cbp.DstAddress.Pack());

                (int srcBaseOffset, int srcSize) = srcCalculator.GetRectangleRange(src.RegionX, src.RegionY, cbp.XCount, cbp.YCount);
                (int dstBaseOffset, int dstSize) = dstCalculator.GetRectangleRange(dst.RegionX, dst.RegionY, cbp.XCount, cbp.YCount);

                ReadOnlySpan <byte> srcSpan = _context.PhysicalMemory.GetSpan(srcBaseAddress + (ulong)srcBaseOffset, srcSize);
                Span <byte>         dstSpan = _context.PhysicalMemory.GetSpan(dstBaseAddress + (ulong)dstBaseOffset, dstSize).ToArray();

                bool completeSource = src.RegionX == 0 && src.RegionY == 0 && src.Width == cbp.XCount && src.Height == cbp.YCount;
                bool completeDest   = dst.RegionX == 0 && dst.RegionY == 0 && dst.Width == cbp.XCount && dst.Height == cbp.YCount;

                if (completeSource && completeDest && srcCalculator.LayoutMatches(dstCalculator))
                {
                    srcSpan.CopyTo(dstSpan); // No layout conversion has to be performed, just copy the data entirely.
                }
                else
                {
                    unsafe bool Convert <T>(Span <byte> dstSpan, ReadOnlySpan <byte> srcSpan) where T : unmanaged
                    {
                        fixed(byte *dstPtr = dstSpan, srcPtr = srcSpan)
                        {
                            byte *dstBase = dstPtr - dstBaseOffset; // Layout offset is relative to the base, so we need to subtract the span's offset.
                            byte *srcBase = srcPtr - srcBaseOffset;

                            for (int y = 0; y < cbp.YCount; y++)
                            {
                                srcCalculator.SetY(src.RegionY + y);
                                dstCalculator.SetY(dst.RegionY + y);

                                for (int x = 0; x < cbp.XCount; x++)
                                {
                                    int srcOffset = srcCalculator.GetOffset(src.RegionX + x);
                                    int dstOffset = dstCalculator.GetOffset(dst.RegionX + x);

                                    *(T *)(dstBase + dstOffset) = *(T *)(srcBase + srcOffset);
                                }
                            }
                        }

                        return(true);
                    }

                    bool _ = srcBpp switch
                    {
                        1 => Convert <byte>(dstSpan, srcSpan),
                        2 => Convert <ushort>(dstSpan, srcSpan),
                        4 => Convert <uint>(dstSpan, srcSpan),
                        8 => Convert <ulong>(dstSpan, srcSpan),
                        12 => Convert <Bpp12Pixel>(dstSpan, srcSpan),
                        16 => Convert <Vector128 <byte> >(dstSpan, srcSpan),
                        _ => throw new NotSupportedException($"Unable to copy ${srcBpp} bpp pixel format.")
                    };
                }

                _context.PhysicalMemory.Write(dstBaseAddress + (ulong)dstBaseOffset, dstSpan);
            }
            else
            {
                // Buffer to buffer copy.
                BufferManager.CopyBuffer(cbp.SrcAddress, cbp.DstAddress, (uint)size);
            }
        }
Esempio n. 11
0
        /// <summary>
        /// Updates host viewport transform and clipping state based on current GPU state.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        private void UpdateViewportTransform(GpuState state)
        {
            DepthMode depthMode = state.Get <DepthMode>(MethodOffset.DepthMode);

            _context.Renderer.Pipeline.SetDepthMode(depthMode);

            YControl yControl = state.Get <YControl>(MethodOffset.YControl);

            bool   flipY  = yControl.HasFlag(YControl.NegateY);
            Origin origin = yControl.HasFlag(YControl.TriangleRastFlip) ? Origin.LowerLeft : Origin.UpperLeft;

            _context.Renderer.Pipeline.SetOrigin(origin);

            // The triangle rast flip flag only affects rasterization, the viewport is not flipped.
            // Setting the origin mode to upper left on the host, however, not only affects rasterization,
            // but also flips the viewport.
            // We negate the effects of flipping the viewport by flipping it again using the viewport swizzle.
            if (origin == Origin.UpperLeft)
            {
                flipY = !flipY;
            }

            Span <Viewport> viewports = stackalloc Viewport[Constants.TotalViewports];

            for (int index = 0; index < Constants.TotalViewports; index++)
            {
                var transform = state.Get <ViewportTransform>(MethodOffset.ViewportTransform, index);
                var extents   = state.Get <ViewportExtents>  (MethodOffset.ViewportExtents, index);

                float x = transform.TranslateX - MathF.Abs(transform.ScaleX);
                float y = transform.TranslateY - MathF.Abs(transform.ScaleY);

                float width  = MathF.Abs(transform.ScaleX) * 2;
                float height = MathF.Abs(transform.ScaleY) * 2;

                float scale = TextureManager.RenderTargetScale;
                if (scale != 1f)
                {
                    x      *= scale;
                    y      *= scale;
                    width  *= scale;
                    height *= scale;
                }

                RectangleF region = new RectangleF(x, y, width, height);

                ViewportSwizzle swizzleX = transform.UnpackSwizzleX();
                ViewportSwizzle swizzleY = transform.UnpackSwizzleY();
                ViewportSwizzle swizzleZ = transform.UnpackSwizzleZ();
                ViewportSwizzle swizzleW = transform.UnpackSwizzleW();

                if (transform.ScaleX < 0)
                {
                    swizzleX ^= ViewportSwizzle.NegativeFlag;
                }

                if (flipY)
                {
                    swizzleY ^= ViewportSwizzle.NegativeFlag;
                }

                if (transform.ScaleY < 0)
                {
                    swizzleY ^= ViewportSwizzle.NegativeFlag;
                }

                if (transform.ScaleZ < 0)
                {
                    swizzleZ ^= ViewportSwizzle.NegativeFlag;
                }

                viewports[index] = new Viewport(
                    region,
                    swizzleX,
                    swizzleY,
                    swizzleZ,
                    swizzleW,
                    extents.DepthNear,
                    extents.DepthFar);
            }

            _context.Renderer.Pipeline.SetViewports(0, viewports);
        }
Esempio n. 12
0
        /// <summary>
        /// Dispatches compute work.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        /// <param name="argument">Method call argument</param>
        public void Dispatch(GpuState state, int argument)
        {
            uint dispatchParamsAddress = (uint)state.Get <int>(MethodOffset.DispatchParamsAddress);

            var dispatchParams = _context.MemoryAccessor.Read <ComputeParams>((ulong)dispatchParamsAddress << 8);

            GpuVa shaderBaseAddress = state.Get <GpuVa>(MethodOffset.ShaderBaseAddress);

            ulong shaderGpuVa = shaderBaseAddress.Pack() + (uint)dispatchParams.ShaderOffset;

            // Note: A size of 0 is also invalid, the size must be at least 1.
            int sharedMemorySize = Math.Clamp(dispatchParams.SharedMemorySize & 0xffff, 1, _context.Capabilities.MaximumComputeSharedMemorySize);

            ComputeShader cs = ShaderCache.GetComputeShader(
                shaderGpuVa,
                sharedMemorySize,
                dispatchParams.UnpackBlockSizeX(),
                dispatchParams.UnpackBlockSizeY(),
                dispatchParams.UnpackBlockSizeZ());

            _context.Renderer.Pipeline.SetProgram(cs.HostProgram);

            var samplerPool = state.Get <PoolState>(MethodOffset.SamplerPoolState);

            TextureManager.SetComputeSamplerPool(samplerPool.Address.Pack(), samplerPool.MaximumId, dispatchParams.SamplerIndex);

            var texturePool = state.Get <PoolState>(MethodOffset.TexturePoolState);

            TextureManager.SetComputeTexturePool(texturePool.Address.Pack(), texturePool.MaximumId);

            TextureManager.SetComputeTextureBufferIndex(state.Get <int>(MethodOffset.TextureBufferIndex));

            ShaderProgramInfo info = cs.Shader.Program.Info;

            uint sbEnableMask = 0;
            uint ubEnableMask = dispatchParams.UnpackUniformBuffersEnableMask();

            for (int index = 0; index < dispatchParams.UniformBuffers.Length; index++)
            {
                if ((ubEnableMask & (1 << index)) == 0)
                {
                    continue;
                }

                ulong gpuVa = dispatchParams.UniformBuffers[index].PackAddress();
                ulong size  = dispatchParams.UniformBuffers[index].UnpackSize();

                BufferManager.SetComputeUniformBuffer(index, gpuVa, size);
            }

            for (int index = 0; index < info.SBuffers.Count; index++)
            {
                BufferDescriptor sb = info.SBuffers[index];

                sbEnableMask |= 1u << sb.Slot;

                ulong sbDescAddress = BufferManager.GetComputeUniformBufferAddress(0);

                int sbDescOffset = 0x310 + sb.Slot * 0x10;

                sbDescAddress += (ulong)sbDescOffset;

                ReadOnlySpan <byte> sbDescriptorData = _context.PhysicalMemory.GetSpan(sbDescAddress, 0x10);

                SbDescriptor sbDescriptor = MemoryMarshal.Cast <byte, SbDescriptor>(sbDescriptorData)[0];

                BufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size);
            }

            ubEnableMask = 0;

            for (int index = 0; index < info.CBuffers.Count; index++)
            {
                ubEnableMask |= 1u << info.CBuffers[index].Slot;
            }

            BufferManager.SetComputeStorageBufferEnableMask(sbEnableMask);
            BufferManager.SetComputeUniformBufferEnableMask(ubEnableMask);

            var textureBindings = new TextureBindingInfo[info.Textures.Count];

            for (int index = 0; index < info.Textures.Count; index++)
            {
                var descriptor = info.Textures[index];

                Target target = GetTarget(descriptor.Type);

                if (descriptor.IsBindless)
                {
                    textureBindings[index] = new TextureBindingInfo(target, descriptor.CbufOffset, descriptor.CbufSlot);
                }
                else
                {
                    textureBindings[index] = new TextureBindingInfo(target, descriptor.HandleIndex);
                }
            }

            TextureManager.SetComputeTextures(textureBindings);

            var imageBindings = new TextureBindingInfo[info.Images.Count];

            for (int index = 0; index < info.Images.Count; index++)
            {
                var descriptor = info.Images[index];

                Target target = GetTarget(descriptor.Type);

                imageBindings[index] = new TextureBindingInfo(target, descriptor.HandleIndex);
            }

            TextureManager.SetComputeImages(imageBindings);

            BufferManager.CommitComputeBindings();
            TextureManager.CommitComputeBindings();

            _context.Renderer.Pipeline.DispatchCompute(
                dispatchParams.UnpackGridSizeX(),
                dispatchParams.UnpackGridSizeY(),
                dispatchParams.UnpackGridSizeZ());

            UpdateShaderState(state);
        }
Esempio n. 13
0
        /// <summary>
        /// Performs a texture to texture copy.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        /// <param name="argument">Method call argument</param>
        private void CopyTexture(GpuState state, int argument)
        {
            var dstCopyTexture = state.Get <CopyTexture>(MethodOffset.CopyDstTexture);
            var srcCopyTexture = state.Get <CopyTexture>(MethodOffset.CopySrcTexture);

            var region = state.Get <CopyRegion>(MethodOffset.CopyRegion);

            var control = state.Get <CopyTextureControl>(MethodOffset.CopyTextureControl);

            int srcX1 = (int)(region.SrcXF >> 32);
            int srcY1 = (int)(region.SrcYF >> 32);

            int srcX2 = (int)((region.SrcXF + region.SrcWidthRF * region.DstWidth) >> 32);
            int srcY2 = (int)((region.SrcYF + region.SrcHeightRF * region.DstHeight) >> 32);

            int dstX1 = region.DstX;
            int dstY1 = region.DstY;

            int dstX2 = region.DstX + region.DstWidth;
            int dstY2 = region.DstY + region.DstHeight;

            // The source and destination textures should at least be as big as the region being requested.
            // The hints will only resize within alignment constraints, so out of bound copies won't resize in most cases.
            var srcHint = new Size(srcX2, srcY2, 1);
            var dstHint = new Size(dstX2, dstY2, 1);

            var srcCopyTextureFormat = srcCopyTexture.Format.Convert();

            Texture srcTexture = TextureManager.FindOrCreateTexture(srcCopyTexture, srcCopyTextureFormat, true, srcHint);

            if (srcTexture == null)
            {
                return;
            }

            // When the source texture that was found has a depth format,
            // we must enforce the target texture also has a depth format,
            // as copies between depth and color formats are not allowed.
            FormatInfo dstCopyTextureFormat;

            if (srcTexture.Format.IsDepthOrStencil())
            {
                dstCopyTextureFormat = srcCopyTextureFormat;
            }
            else
            {
                dstCopyTextureFormat = dstCopyTexture.Format.Convert();
            }

            Texture dstTexture = TextureManager.FindOrCreateTexture(dstCopyTexture, dstCopyTextureFormat, srcTexture.ScaleMode == TextureScaleMode.Scaled, dstHint);

            if (dstTexture == null)
            {
                return;
            }

            if (srcTexture.ScaleFactor != dstTexture.ScaleFactor)
            {
                srcTexture.PropagateScale(dstTexture);
            }

            float scale = srcTexture.ScaleFactor; // src and dest scales are identical now.

            Extents2D srcRegion = new Extents2D(
                (int)Math.Ceiling(scale * (srcX1 / srcTexture.Info.SamplesInX)),
                (int)Math.Ceiling(scale * (srcY1 / srcTexture.Info.SamplesInY)),
                (int)Math.Ceiling(scale * (srcX2 / srcTexture.Info.SamplesInX)),
                (int)Math.Ceiling(scale * (srcY2 / srcTexture.Info.SamplesInY)));

            Extents2D dstRegion = new Extents2D(
                (int)Math.Ceiling(scale * (dstX1 / dstTexture.Info.SamplesInX)),
                (int)Math.Ceiling(scale * (dstY1 / dstTexture.Info.SamplesInY)),
                (int)Math.Ceiling(scale * (dstX2 / dstTexture.Info.SamplesInX)),
                (int)Math.Ceiling(scale * (dstY2 / dstTexture.Info.SamplesInY)));

            bool linearFilter = control.UnpackLinearFilter();

            srcTexture.HostTexture.CopyTo(dstTexture.HostTexture, srcRegion, dstRegion, linearFilter);

            // For an out of bounds copy, we must ensure that the copy wraps to the next line,
            // so for a copy from a 64x64 texture, in the region [32, 96[, there are 32 pixels that are
            // outside the bounds of the texture. We fill the destination with the first 32 pixels
            // of the next line on the source texture.
            // This can be emulated with 2 copies (the first copy handles the region inside the bounds,
            // the second handles the region outside of the bounds).
            // We must also extend the source texture by one line to ensure we can wrap on the last line.
            // This is required by the (guest) OpenGL driver.
            if (srcX2 / srcTexture.Info.SamplesInX > srcTexture.Info.Width)
            {
                srcCopyTexture.Height++;

                srcTexture = TextureManager.FindOrCreateTexture(srcCopyTexture, srcCopyTextureFormat, srcTexture.ScaleMode == TextureScaleMode.Scaled, srcHint);
                if (srcTexture.ScaleFactor != dstTexture.ScaleFactor)
                {
                    srcTexture.PropagateScale(dstTexture);
                }

                srcRegion = new Extents2D(
                    (int)Math.Ceiling(scale * ((srcX1 / srcTexture.Info.SamplesInX) - srcTexture.Info.Width)),
                    (int)Math.Ceiling(scale * ((srcY1 / srcTexture.Info.SamplesInY) + 1)),
                    (int)Math.Ceiling(scale * ((srcX2 / srcTexture.Info.SamplesInX) - srcTexture.Info.Width)),
                    (int)Math.Ceiling(scale * ((srcY2 / srcTexture.Info.SamplesInY) + 1)));

                srcTexture.HostTexture.CopyTo(dstTexture.HostTexture, srcRegion, dstRegion, linearFilter);
            }

            dstTexture.SignalModified();
        }
Esempio n. 14
0
        /// <summary>
        /// Performs a texture to texture copy.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        /// <param name="argument">Method call argument</param>
        private void CopyTexture(GpuState state, int argument)
        {
            var dstCopyTexture = state.Get <CopyTexture>(MethodOffset.CopyDstTexture);
            var srcCopyTexture = state.Get <CopyTexture>(MethodOffset.CopySrcTexture);

            var region = state.Get <CopyRegion>(MethodOffset.CopyRegion);

            var control = state.Get <CopyTextureControl>(MethodOffset.CopyTextureControl);

            int srcX1 = (int)(region.SrcXF >> 32);
            int srcY1 = (int)(region.SrcYF >> 32);

            int srcX2 = (int)((region.SrcXF + region.SrcWidthRF * region.DstWidth) >> 32);
            int srcY2 = (int)((region.SrcYF + region.SrcHeightRF * region.DstHeight) >> 32);

            int dstX1 = region.DstX;
            int dstY1 = region.DstY;

            int dstX2 = region.DstX + region.DstWidth;
            int dstY2 = region.DstY + region.DstHeight;

            // The source and destination textures should at least be as big as the region being requested.
            // The hints will only resize within alignment constraints, so out of bound copies won't resize in most cases.
            var srcHint = new Size(srcX2, srcY2, 1);
            var dstHint = new Size(dstX2, dstY2, 1);

            var srcCopyTextureFormat = srcCopyTexture.Format.Convert();

            int srcWidthAligned = srcCopyTexture.Stride / srcCopyTextureFormat.BytesPerPixel;

            ulong offset = 0;

            // For an out of bounds copy, we must ensure that the copy wraps to the next line,
            // so for a copy from a 64x64 texture, in the region [32, 96[, there are 32 pixels that are
            // outside the bounds of the texture. We fill the destination with the first 32 pixels
            // of the next line on the source texture.
            // This can be done by simply adding an offset to the texture address, so that the initial
            // gap is skipped and the copy is inside bounds again.
            // This is required by the proprietary guest OpenGL driver.
            if (srcCopyTexture.LinearLayout && srcCopyTexture.Width == srcX2 && srcX2 > srcWidthAligned && srcX1 > 0)
            {
                offset = (ulong)(srcX1 * srcCopyTextureFormat.BytesPerPixel);
                srcCopyTexture.Width -= srcX1;
                srcX2 -= srcX1;
                srcX1  = 0;
            }

            Texture srcTexture = TextureManager.FindOrCreateTexture(srcCopyTexture, offset, srcCopyTextureFormat, true, srcHint);

            if (srcTexture == null)
            {
                return;
            }

            // When the source texture that was found has a depth format,
            // we must enforce the target texture also has a depth format,
            // as copies between depth and color formats are not allowed.
            FormatInfo dstCopyTextureFormat;

            if (srcTexture.Format.IsDepthOrStencil())
            {
                dstCopyTextureFormat = srcTexture.Info.FormatInfo;
            }
            else
            {
                dstCopyTextureFormat = dstCopyTexture.Format.Convert();
            }

            Texture dstTexture = TextureManager.FindOrCreateTexture(dstCopyTexture, 0, dstCopyTextureFormat, srcTexture.ScaleMode == TextureScaleMode.Scaled, dstHint);

            if (dstTexture == null)
            {
                return;
            }

            float scale    = srcTexture.ScaleFactor;
            float dstScale = dstTexture.ScaleFactor;

            Extents2D srcRegion = new Extents2D(
                (int)Math.Ceiling(scale * (srcX1 / srcTexture.Info.SamplesInX)),
                (int)Math.Ceiling(scale * (srcY1 / srcTexture.Info.SamplesInY)),
                (int)Math.Ceiling(scale * (srcX2 / srcTexture.Info.SamplesInX)),
                (int)Math.Ceiling(scale * (srcY2 / srcTexture.Info.SamplesInY)));

            Extents2D dstRegion = new Extents2D(
                (int)Math.Ceiling(dstScale * (dstX1 / dstTexture.Info.SamplesInX)),
                (int)Math.Ceiling(dstScale * (dstY1 / dstTexture.Info.SamplesInY)),
                (int)Math.Ceiling(dstScale * (dstX2 / dstTexture.Info.SamplesInX)),
                (int)Math.Ceiling(dstScale * (dstY2 / dstTexture.Info.SamplesInY)));

            bool linearFilter = control.UnpackLinearFilter();

            srcTexture.HostTexture.CopyTo(dstTexture.HostTexture, srcRegion, dstRegion, linearFilter);

            dstTexture.SignalModified();
        }
Esempio n. 15
0
        /// <summary>
        /// Finishes the draw call.
        /// This draws geometry on the bound buffers based on the current GPU state.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        /// <param name="firstIndex">Index of the first index buffer element used on the draw</param>
        /// <param name="indexCount">Number of index buffer elements used on the draw</param>
        private void DrawEnd(GpuState state, int firstIndex, int indexCount)
        {
            ConditionalRenderEnabled renderEnable = GetRenderEnable(state);

            if (renderEnable == ConditionalRenderEnabled.False || _instancedDrawPending)
            {
                if (renderEnable == ConditionalRenderEnabled.False)
                {
                    PerformDeferredDraws();
                }

                _drawIndexed = false;

                if (renderEnable == ConditionalRenderEnabled.Host)
                {
                    _context.Renderer.Pipeline.EndHostConditionalRendering();
                }

                return;
            }

            UpdateState(state, firstIndex, indexCount);

            bool instanced = _vsUsesInstanceId || _isAnyVbInstanced;

            if (instanced)
            {
                _instancedDrawPending = true;

                _instancedIndexed = _drawIndexed;

                _instancedFirstIndex    = firstIndex;
                _instancedFirstVertex   = state.Get <int>(MethodOffset.FirstVertex);
                _instancedFirstInstance = state.Get <int>(MethodOffset.FirstInstance);

                _instancedIndexCount = indexCount;

                var drawState = state.Get <VertexBufferDrawState>(MethodOffset.VertexBufferDrawState);

                _instancedDrawStateFirst = drawState.First;
                _instancedDrawStateCount = drawState.Count;

                _drawIndexed = false;

                if (renderEnable == ConditionalRenderEnabled.Host)
                {
                    _context.Renderer.Pipeline.EndHostConditionalRendering();
                }

                return;
            }

            int firstInstance = state.Get <int>(MethodOffset.FirstInstance);

            int inlineIndexCount = _ibStreamer.GetAndResetInlineIndexCount();

            if (inlineIndexCount != 0)
            {
                int firstVertex = state.Get <int>(MethodOffset.FirstVertex);

                BufferRange br = new BufferRange(_ibStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4);

                state.Channel.BufferManager.SetIndexBuffer(br, IndexType.UInt);

                _context.Renderer.Pipeline.DrawIndexed(
                    inlineIndexCount,
                    1,
                    firstIndex,
                    firstVertex,
                    firstInstance);
            }
            else if (_drawIndexed)
            {
                int firstVertex = state.Get <int>(MethodOffset.FirstVertex);

                _context.Renderer.Pipeline.DrawIndexed(
                    indexCount,
                    1,
                    firstIndex,
                    firstVertex,
                    firstInstance);
            }
            else
            {
                var drawState = state.Get <VertexBufferDrawState>(MethodOffset.VertexBufferDrawState);

                _context.Renderer.Pipeline.Draw(
                    drawState.Count,
                    1,
                    drawState.First,
                    firstInstance);
            }

            _drawIndexed = false;

            if (renderEnable == ConditionalRenderEnabled.Host)
            {
                _context.Renderer.Pipeline.EndHostConditionalRendering();
            }
        }
Esempio n. 16
0
        /// <summary>
        /// Updates host depth clamp state based on current GPU state.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        private void UpdateDepthClampState(GpuState state)
        {
            ViewVolumeClipControl clip = state.Get <ViewVolumeClipControl>(MethodOffset.ViewVolumeClipControl);

            _context.Renderer.Pipeline.SetDepthClamp((clip & ViewVolumeClipControl.DepthClampDisabled) == 0);
        }
Esempio n. 17
0
        /// <summary>
        /// Dispatches compute work.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        /// <param name="argument">Method call argument</param>
        public void Dispatch(GpuState state, int argument)
        {
            uint qmdAddress = (uint)state.Get <int>(MethodOffset.DispatchParamsAddress);

            var qmd = _context.MemoryManager.Read <ComputeQmd>((ulong)qmdAddress << 8);

            GpuVa shaderBaseAddress = state.Get <GpuVa>(MethodOffset.ShaderBaseAddress);

            ulong shaderGpuVa = shaderBaseAddress.Pack() + (uint)qmd.ProgramOffset;

            int localMemorySize = qmd.ShaderLocalMemoryLowSize + qmd.ShaderLocalMemoryHighSize;

            int sharedMemorySize = Math.Min(qmd.SharedMemorySize, _context.Capabilities.MaximumComputeSharedMemorySize);

            for (int index = 0; index < Constants.TotalCpUniformBuffers; index++)
            {
                if (!qmd.ConstantBufferValid(index))
                {
                    continue;
                }

                ulong gpuVa = (uint)qmd.ConstantBufferAddrLower(index) | (ulong)qmd.ConstantBufferAddrUpper(index) << 32;
                ulong size  = (ulong)qmd.ConstantBufferSize(index);

                BufferManager.SetComputeUniformBuffer(index, gpuVa, size);
            }

            ShaderBundle cs = ShaderCache.GetComputeShader(
                state,
                shaderGpuVa,
                qmd.CtaThreadDimension0,
                qmd.CtaThreadDimension1,
                qmd.CtaThreadDimension2,
                localMemorySize,
                sharedMemorySize);

            _context.Renderer.Pipeline.SetProgram(cs.HostProgram);

            var samplerPool = state.Get <PoolState>(MethodOffset.SamplerPoolState);
            var texturePool = state.Get <PoolState>(MethodOffset.TexturePoolState);

            TextureManager.SetComputeSamplerPool(samplerPool.Address.Pack(), samplerPool.MaximumId, qmd.SamplerIndex);
            TextureManager.SetComputeTexturePool(texturePool.Address.Pack(), texturePool.MaximumId);
            TextureManager.SetComputeTextureBufferIndex(state.Get <int>(MethodOffset.TextureBufferIndex));

            ShaderProgramInfo info = cs.Shaders[0].Info;

            for (int index = 0; index < info.CBuffers.Count; index++)
            {
                BufferDescriptor cb = info.CBuffers[index];

                // NVN uses the "hardware" constant buffer for anything that is less than 8,
                // and those are already bound above.
                // Anything greater than or equal to 8 uses the emulated constant buffers.
                // They are emulated using global memory loads.
                if (cb.Slot < 8)
                {
                    continue;
                }

                ulong cbDescAddress = BufferManager.GetComputeUniformBufferAddress(0);

                int cbDescOffset = 0x260 + (cb.Slot - 8) * 0x10;

                cbDescAddress += (ulong)cbDescOffset;

                SbDescriptor cbDescriptor = _context.PhysicalMemory.Read <SbDescriptor>(cbDescAddress);

                BufferManager.SetComputeUniformBuffer(cb.Slot, cbDescriptor.PackAddress(), (uint)cbDescriptor.Size);
            }

            for (int index = 0; index < info.SBuffers.Count; index++)
            {
                BufferDescriptor sb = info.SBuffers[index];

                ulong sbDescAddress = BufferManager.GetComputeUniformBufferAddress(0);

                int sbDescOffset = 0x310 + sb.Slot * 0x10;

                sbDescAddress += (ulong)sbDescOffset;

                SbDescriptor sbDescriptor = _context.PhysicalMemory.Read <SbDescriptor>(sbDescAddress);

                BufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags);
            }

            BufferManager.SetComputeStorageBufferBindings(info.SBuffers);
            BufferManager.SetComputeUniformBufferBindings(info.CBuffers);

            var textureBindings = new TextureBindingInfo[info.Textures.Count];

            for (int index = 0; index < info.Textures.Count; index++)
            {
                var descriptor = info.Textures[index];

                Target target = ShaderTexture.GetTarget(descriptor.Type);

                textureBindings[index] = new TextureBindingInfo(
                    target,
                    descriptor.Binding,
                    descriptor.CbufSlot,
                    descriptor.HandleIndex,
                    descriptor.Flags);
            }

            TextureManager.SetComputeTextures(textureBindings);

            var imageBindings = new TextureBindingInfo[info.Images.Count];

            for (int index = 0; index < info.Images.Count; index++)
            {
                var descriptor = info.Images[index];

                Target target = ShaderTexture.GetTarget(descriptor.Type);
                Format format = ShaderTexture.GetFormat(descriptor.Format);

                imageBindings[index] = new TextureBindingInfo(
                    target,
                    format,
                    descriptor.Binding,
                    descriptor.CbufSlot,
                    descriptor.HandleIndex,
                    descriptor.Flags);
            }

            TextureManager.SetComputeImages(imageBindings);

            TextureManager.CommitComputeBindings();
            BufferManager.CommitComputeBindings();

            _context.Renderer.Pipeline.DispatchCompute(
                qmd.CtaRasterWidth,
                qmd.CtaRasterHeight,
                qmd.CtaRasterDepth);

            _forceShaderUpdate = true;
        }
Esempio n. 18
0
        /// <summary>
        /// Updates host viewport transform and clipping state based on current GPU state.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        private void UpdateViewportTransform(GpuState state)
        {
            var yControl = state.Get <YControl> (MethodOffset.YControl);
            var face     = state.Get <FaceState>(MethodOffset.FaceState);

            UpdateFrontFace(yControl, face.FrontFace);

            bool flipY = yControl.HasFlag(YControl.NegateY);

            Span <Viewport> viewports = stackalloc Viewport[Constants.TotalViewports];

            for (int index = 0; index < Constants.TotalViewports; index++)
            {
                var transform = state.Get <ViewportTransform>(MethodOffset.ViewportTransform, index);
                var extents   = state.Get <ViewportExtents>  (MethodOffset.ViewportExtents, index);

                float scaleX = MathF.Abs(transform.ScaleX);
                float scaleY = transform.ScaleY;

                if (flipY)
                {
                    scaleY = -scaleY;
                }

                if (!_context.Capabilities.SupportsViewportSwizzle && transform.UnpackSwizzleY() == ViewportSwizzle.NegativeY)
                {
                    scaleY = -scaleY;
                }

                if (index == 0)
                {
                    // Try to guess the depth mode being used on the high level API
                    // based on current transform.
                    // It is setup like so by said APIs:
                    // If depth mode is ZeroToOne:
                    //  TranslateZ = Near
                    //  ScaleZ = Far - Near
                    // If depth mode is MinusOneToOne:
                    //  TranslateZ = (Near + Far) / 2
                    //  ScaleZ = (Far - Near) / 2
                    // DepthNear/Far are sorted such as that Near is always less than Far.
                    DepthMode depthMode = extents.DepthNear != transform.TranslateZ &&
                                          extents.DepthFar != transform.TranslateZ ? DepthMode.MinusOneToOne : DepthMode.ZeroToOne;

                    _context.Renderer.Pipeline.SetDepthMode(depthMode);
                }

                float x = transform.TranslateX - scaleX;
                float y = transform.TranslateY - scaleY;

                float width  = scaleX * 2;
                float height = scaleY * 2;

                float scale = state.Channel.TextureManager.RenderTargetScale;
                if (scale != 1f)
                {
                    x      *= scale;
                    y      *= scale;
                    width  *= scale;
                    height *= scale;
                }

                RectangleF region = new RectangleF(x, y, width, height);

                ViewportSwizzle swizzleX = transform.UnpackSwizzleX();
                ViewportSwizzle swizzleY = transform.UnpackSwizzleY();
                ViewportSwizzle swizzleZ = transform.UnpackSwizzleZ();
                ViewportSwizzle swizzleW = transform.UnpackSwizzleW();

                float depthNear = extents.DepthNear;
                float depthFar  = extents.DepthFar;

                if (transform.ScaleZ < 0)
                {
                    float temp = depthNear;
                    depthNear = depthFar;
                    depthFar  = temp;
                }

                viewports[index] = new Viewport(region, swizzleX, swizzleY, swizzleZ, swizzleW, depthNear, depthFar);
            }

            _context.Renderer.Pipeline.SetViewports(0, viewports);
        }
Esempio n. 19
0
        /// <summary>
        /// Updates host shaders based on the guest GPU state.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        private void UpdateShaderState(GpuState state)
        {
            ShaderAddresses addresses = new ShaderAddresses();

            Span <ShaderAddresses> addressesSpan = MemoryMarshal.CreateSpan(ref addresses, 1);

            Span <ulong> addressesArray = MemoryMarshal.Cast <ShaderAddresses, ulong>(addressesSpan);

            ulong baseAddress = state.Get <GpuVa>(MethodOffset.ShaderBaseAddress).Pack();

            for (int index = 0; index < 6; index++)
            {
                var shader = state.Get <ShaderState>(MethodOffset.ShaderState, index);

                if (!shader.UnpackEnable() && index != 1)
                {
                    continue;
                }

                addressesArray[index] = baseAddress + shader.Offset;
            }

            GraphicsShader gs = ShaderCache.GetGraphicsShader(state, addresses);

            _vsUsesInstanceId = gs.Shaders[0]?.Program.Info.UsesInstanceId ?? false;

            for (int stage = 0; stage < Constants.ShaderStages; stage++)
            {
                ShaderProgramInfo info = gs.Shaders[stage]?.Program.Info;

                _currentProgramInfo[stage] = info;

                if (info == null)
                {
                    continue;
                }

                var textureBindings = new TextureBindingInfo[info.Textures.Count];

                for (int index = 0; index < info.Textures.Count; index++)
                {
                    var descriptor = info.Textures[index];

                    Target target = GetTarget(descriptor.Type);

                    if (descriptor.IsBindless)
                    {
                        textureBindings[index] = new TextureBindingInfo(target, descriptor.CbufSlot, descriptor.CbufOffset);
                    }
                    else
                    {
                        textureBindings[index] = new TextureBindingInfo(target, descriptor.HandleIndex);
                    }
                }

                TextureManager.SetGraphicsTextures(stage, textureBindings);

                var imageBindings = new TextureBindingInfo[info.Images.Count];

                for (int index = 0; index < info.Images.Count; index++)
                {
                    var descriptor = info.Images[index];

                    Target target = GetTarget(descriptor.Type);

                    imageBindings[index] = new TextureBindingInfo(target, descriptor.HandleIndex);
                }

                TextureManager.SetGraphicsImages(stage, imageBindings);

                uint sbEnableMask = 0;
                uint ubEnableMask = 0;

                for (int index = 0; index < info.SBuffers.Count; index++)
                {
                    sbEnableMask |= 1u << info.SBuffers[index].Slot;
                }

                for (int index = 0; index < info.CBuffers.Count; index++)
                {
                    ubEnableMask |= 1u << info.CBuffers[index].Slot;
                }

                BufferManager.SetGraphicsStorageBufferEnableMask(stage, sbEnableMask);
                BufferManager.SetGraphicsUniformBufferEnableMask(stage, ubEnableMask);
            }

            _context.Renderer.Pipeline.SetProgram(gs.HostProgram);
        }
Esempio n. 20
0
        /// <summary>
        /// Updates host logical operation state, based on guest state.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        public void UpdateLogicOpState(GpuState state)
        {
            LogicalOpState logicOpState = state.Get <LogicalOpState>(MethodOffset.LogicOpState);

            _context.Renderer.Pipeline.SetLogicOpState(logicOpState.Enable, logicOpState.LogicalOp);
        }
Esempio n. 21
0
        /// <summary>
        /// Updates host state based on the current guest GPU state.
        /// </summary>
        /// <param name="state">Guest GPU state</param>
        private void UpdateState(GpuState state)
        {
            bool tfEnable = state.Get <Boolean32>(MethodOffset.TfEnable);

            if (!tfEnable && _prevTfEnable)
            {
                _context.Renderer.Pipeline.EndTransformFeedback();
                _prevTfEnable = false;
            }

            // Shaders must be the first one to be updated if modified, because
            // some of the other state depends on information from the currently
            // bound shaders.
            if (state.QueryModified(MethodOffset.ShaderBaseAddress, MethodOffset.ShaderState) || _forceShaderUpdate)
            {
                _forceShaderUpdate = false;

                UpdateShaderState(state);
            }

            if (state.QueryModified(MethodOffset.TfBufferState))
            {
                UpdateTfBufferState(state);
            }

            if (state.QueryModified(MethodOffset.ClipDistanceEnable))
            {
                UpdateUserClipState(state);
            }

            if (state.QueryModified(MethodOffset.RasterizeEnable))
            {
                UpdateRasterizerState(state);
            }

            if (state.QueryModified(MethodOffset.RtColorState,
                                    MethodOffset.RtDepthStencilState,
                                    MethodOffset.RtControl,
                                    MethodOffset.RtDepthStencilSize,
                                    MethodOffset.RtDepthStencilEnable))
            {
                UpdateRenderTargetState(state, useControl: true);
            }

            if (state.QueryModified(MethodOffset.ScissorState))
            {
                UpdateScissorState(state);
            }

            if (state.QueryModified(MethodOffset.ViewVolumeClipControl))
            {
                UpdateDepthClampState(state);
            }

            if (state.QueryModified(MethodOffset.DepthTestEnable,
                                    MethodOffset.DepthWriteEnable,
                                    MethodOffset.DepthTestFunc))
            {
                UpdateDepthTestState(state);
            }

            if (state.QueryModified(MethodOffset.DepthMode,
                                    MethodOffset.ViewportTransform,
                                    MethodOffset.ViewportExtents))
            {
                UpdateViewportTransform(state);
            }

            if (state.QueryModified(MethodOffset.DepthBiasState,
                                    MethodOffset.DepthBiasFactor,
                                    MethodOffset.DepthBiasUnits,
                                    MethodOffset.DepthBiasClamp))
            {
                UpdateDepthBiasState(state);
            }

            if (state.QueryModified(MethodOffset.StencilBackMasks,
                                    MethodOffset.StencilTestState,
                                    MethodOffset.StencilBackTestState))
            {
                UpdateStencilTestState(state);
            }

            // Pools.
            if (state.QueryModified(MethodOffset.SamplerPoolState, MethodOffset.SamplerIndex))
            {
                UpdateSamplerPoolState(state);
            }

            if (state.QueryModified(MethodOffset.TexturePoolState))
            {
                UpdateTexturePoolState(state);
            }

            // Input assembler state.
            if (state.QueryModified(MethodOffset.VertexAttribState))
            {
                UpdateVertexAttribState(state);
            }

            if (state.QueryModified(MethodOffset.PointSize,
                                    MethodOffset.VertexProgramPointSize,
                                    MethodOffset.PointSpriteEnable,
                                    MethodOffset.PointCoordReplace))
            {
                UpdatePointState(state);
            }

            if (state.QueryModified(MethodOffset.PrimitiveRestartState))
            {
                UpdatePrimitiveRestartState(state);
            }

            if (state.QueryModified(MethodOffset.IndexBufferState))
            {
                UpdateIndexBufferState(state);
            }

            if (state.QueryModified(MethodOffset.VertexBufferDrawState,
                                    MethodOffset.VertexBufferInstanced,
                                    MethodOffset.VertexBufferState,
                                    MethodOffset.VertexBufferEndAddress))
            {
                UpdateVertexBufferState(state);
            }

            if (state.QueryModified(MethodOffset.FaceState))
            {
                UpdateFaceState(state);
            }

            if (state.QueryModified(MethodOffset.RtColorMaskShared, MethodOffset.RtColorMask))
            {
                UpdateRtColorMask(state);
            }

            if (state.QueryModified(MethodOffset.BlendIndependent,
                                    MethodOffset.BlendConstant,
                                    MethodOffset.BlendStateCommon,
                                    MethodOffset.BlendEnableCommon,
                                    MethodOffset.BlendEnable,
                                    MethodOffset.BlendState))
            {
                UpdateBlendState(state);
            }

            if (state.QueryModified(MethodOffset.LogicOpState))
            {
                UpdateLogicOpState(state);
            }

            CommitBindings();

            if (tfEnable && !_prevTfEnable)
            {
                _context.Renderer.Pipeline.BeginTransformFeedback(PrimitiveType.Convert());
                _prevTfEnable = true;
            }
        }
Esempio n. 22
0
        /// <summary>
        /// Updates host shaders based on the guest GPU state.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        private void UpdateShaderState(GpuState state)
        {
            ShaderAddresses addresses = new ShaderAddresses();

            Span <ShaderAddresses> addressesSpan = MemoryMarshal.CreateSpan(ref addresses, 1);

            Span <ulong> addressesArray = MemoryMarshal.Cast <ShaderAddresses, ulong>(addressesSpan);

            ulong baseAddress = state.Get <GpuVa>(MethodOffset.ShaderBaseAddress).Pack();

            for (int index = 0; index < 6; index++)
            {
                var shader = state.Get <ShaderState>(MethodOffset.ShaderState, index);

                if (!shader.UnpackEnable() && index != 1)
                {
                    continue;
                }

                addressesArray[index] = baseAddress + shader.Offset;
            }

            ShaderBundle gs = state.Channel.MemoryManager.Physical.ShaderCache.GetGraphicsShader(state, addresses);

            byte oldVsClipDistancesWritten = _vsClipDistancesWritten;

            _vsUsesInstanceId       = gs.Shaders[0]?.Info.UsesInstanceId ?? false;
            _vsClipDistancesWritten = gs.Shaders[0]?.Info.ClipDistancesWritten ?? 0;

            if (oldVsClipDistancesWritten != _vsClipDistancesWritten)
            {
                UpdateUserClipState(state);
            }

            int storageBufferBindingsCount = 0;
            int uniformBufferBindingsCount = 0;

            for (int stage = 0; stage < Constants.ShaderStages; stage++)
            {
                ShaderProgramInfo info = gs.Shaders[stage]?.Info;

                _currentProgramInfo[stage] = info;

                if (info == null)
                {
                    state.Channel.TextureManager.SetGraphicsTextures(stage, Array.Empty <TextureBindingInfo>());
                    state.Channel.TextureManager.SetGraphicsImages(stage, Array.Empty <TextureBindingInfo>());
                    state.Channel.BufferManager.SetGraphicsStorageBufferBindings(stage, null);
                    state.Channel.BufferManager.SetGraphicsUniformBufferBindings(stage, null);
                    continue;
                }

                var textureBindings = new TextureBindingInfo[info.Textures.Count];

                for (int index = 0; index < info.Textures.Count; index++)
                {
                    var descriptor = info.Textures[index];

                    Target target = ShaderTexture.GetTarget(descriptor.Type);

                    textureBindings[index] = new TextureBindingInfo(
                        target,
                        descriptor.Binding,
                        descriptor.CbufSlot,
                        descriptor.HandleIndex,
                        descriptor.Flags);
                }

                state.Channel.TextureManager.SetGraphicsTextures(stage, textureBindings);

                var imageBindings = new TextureBindingInfo[info.Images.Count];

                for (int index = 0; index < info.Images.Count; index++)
                {
                    var descriptor = info.Images[index];

                    Target target = ShaderTexture.GetTarget(descriptor.Type);
                    Format format = ShaderTexture.GetFormat(descriptor.Format);

                    imageBindings[index] = new TextureBindingInfo(
                        target,
                        format,
                        descriptor.Binding,
                        descriptor.CbufSlot,
                        descriptor.HandleIndex,
                        descriptor.Flags);
                }

                state.Channel.TextureManager.SetGraphicsImages(stage, imageBindings);

                state.Channel.BufferManager.SetGraphicsStorageBufferBindings(stage, info.SBuffers);
                state.Channel.BufferManager.SetGraphicsUniformBufferBindings(stage, info.CBuffers);

                if (info.SBuffers.Count != 0)
                {
                    storageBufferBindingsCount = Math.Max(storageBufferBindingsCount, info.SBuffers.Max(x => x.Binding) + 1);
                }

                if (info.CBuffers.Count != 0)
                {
                    uniformBufferBindingsCount = Math.Max(uniformBufferBindingsCount, info.CBuffers.Max(x => x.Binding) + 1);
                }
            }

            state.Channel.BufferManager.SetGraphicsStorageBufferBindingsCount(storageBufferBindingsCount);
            state.Channel.BufferManager.SetGraphicsUniformBufferBindingsCount(uniformBufferBindingsCount);

            _context.Renderer.Pipeline.SetProgram(gs.HostProgram);
        }
Esempio n. 23
0
        /// <summary>
        /// Updates render targets (color and depth-stencil buffers) based on current render target state.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        /// <param name="useControl">Use draw buffers information from render target control register</param>
        /// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
        private void UpdateRenderTargetState(GpuState state, bool useControl, int singleUse = -1)
        {
            var rtControl = state.Get <RtControl>(MethodOffset.RtControl);

            int count = useControl ? rtControl.UnpackCount() : Constants.TotalRenderTargets;

            var msaaMode = state.Get <TextureMsaaMode>(MethodOffset.RtMsaaMode);

            int samplesInX = msaaMode.SamplesInX();
            int samplesInY = msaaMode.SamplesInY();

            bool changedScale = false;

            for (int index = 0; index < Constants.TotalRenderTargets; index++)
            {
                int rtIndex = useControl ? rtControl.UnpackPermutationIndex(index) : index;

                var colorState = state.Get <RtColorState>(MethodOffset.RtColorState, rtIndex);

                if (index >= count || !IsRtEnabled(colorState))
                {
                    changedScale |= TextureManager.SetRenderTargetColor(index, null);

                    continue;
                }

                Texture color = TextureManager.FindOrCreateTexture(colorState, samplesInX, samplesInY);

                changedScale |= TextureManager.SetRenderTargetColor(index, color);

                if (color != null)
                {
                    color.SignalModified();
                }
            }

            bool dsEnable = state.Get <Boolean32>(MethodOffset.RtDepthStencilEnable);

            Texture depthStencil = null;

            if (dsEnable)
            {
                var dsState = state.Get <RtDepthStencilState>(MethodOffset.RtDepthStencilState);
                var dsSize  = state.Get <Size3D>             (MethodOffset.RtDepthStencilSize);

                depthStencil = TextureManager.FindOrCreateTexture(dsState, dsSize, samplesInX, samplesInY);
            }

            changedScale |= TextureManager.SetRenderTargetDepthStencil(depthStencil);

            if (changedScale)
            {
                TextureManager.UpdateRenderTargetScale(singleUse);
                _context.Renderer.Pipeline.SetRenderTargetScale(TextureManager.RenderTargetScale);

                UpdateViewportTransform(state);
                UpdateScissorState(state);
            }

            if (depthStencil != null)
            {
                depthStencil.SignalModified();
            }
        }
Esempio n. 24
0
        /// <summary>
        /// Performs a texture to texture copy.
        /// </summary>
        /// <param name="state">Current GPU state</param>
        /// <param name="argument">Method call argument</param>
        private void CopyTexture(GpuState state, int argument)
        {
            var dstCopyTexture = state.Get <CopyTexture>(MethodOffset.CopyDstTexture);
            var srcCopyTexture = state.Get <CopyTexture>(MethodOffset.CopySrcTexture);

            Texture srcTexture = TextureManager.FindOrCreateTexture(srcCopyTexture);

            if (srcTexture == null)
            {
                return;
            }

            // When the source texture that was found has a depth format,
            // we must enforce the target texture also has a depth format,
            // as copies between depth and color formats are not allowed.
            dstCopyTexture.Format = srcTexture.Format switch
            {
                Format.S8Uint => RtFormat.S8Uint,
                Format.D16Unorm => RtFormat.D16Unorm,
                Format.D24X8Unorm => RtFormat.D24Unorm,
                Format.D32Float => RtFormat.D32Float,
                Format.D24UnormS8Uint => RtFormat.D24UnormS8Uint,
                Format.D32FloatS8Uint => RtFormat.D32FloatS8Uint,
                _ => dstCopyTexture.Format
            };

            Texture dstTexture = TextureManager.FindOrCreateTexture(dstCopyTexture, srcTexture.ScaleMode == Image.TextureScaleMode.Scaled);

            if (dstTexture == null)
            {
                return;
            }

            if (srcTexture.ScaleFactor != dstTexture.ScaleFactor)
            {
                srcTexture.PropagateScale(dstTexture);
            }

            var control = state.Get <CopyTextureControl>(MethodOffset.CopyTextureControl);

            var region = state.Get <CopyRegion>(MethodOffset.CopyRegion);

            int srcX1 = (int)(region.SrcXF >> 32);
            int srcY1 = (int)(region.SrcYF >> 32);

            int srcX2 = (int)((region.SrcXF + region.SrcWidthRF * region.DstWidth) >> 32);
            int srcY2 = (int)((region.SrcYF + region.SrcHeightRF * region.DstHeight) >> 32);

            int dstX1 = region.DstX;
            int dstY1 = region.DstY;

            int dstX2 = region.DstX + region.DstWidth;
            int dstY2 = region.DstY + region.DstHeight;

            float scale = srcTexture.ScaleFactor; // src and dest scales are identical now.

            Extents2D srcRegion = new Extents2D(
                (int)Math.Ceiling(scale * (srcX1 / srcTexture.Info.SamplesInX)),
                (int)Math.Ceiling(scale * (srcY1 / srcTexture.Info.SamplesInY)),
                (int)Math.Ceiling(scale * (srcX2 / srcTexture.Info.SamplesInX)),
                (int)Math.Ceiling(scale * (srcY2 / srcTexture.Info.SamplesInY)));

            Extents2D dstRegion = new Extents2D(
                (int)Math.Ceiling(scale * (dstX1 / dstTexture.Info.SamplesInX)),
                (int)Math.Ceiling(scale * (dstY1 / dstTexture.Info.SamplesInY)),
                (int)Math.Ceiling(scale * (dstX2 / dstTexture.Info.SamplesInX)),
                (int)Math.Ceiling(scale * (dstY2 / dstTexture.Info.SamplesInY)));

            bool linearFilter = control.UnpackLinearFilter();

            srcTexture.HostTexture.CopyTo(dstTexture.HostTexture, srcRegion, dstRegion, linearFilter);

            // For an out of bounds copy, we must ensure that the copy wraps to the next line,
            // so for a copy from a 64x64 texture, in the region [32, 96[, there are 32 pixels that are
            // outside the bounds of the texture. We fill the destination with the first 32 pixels
            // of the next line on the source texture.
            // This can be emulated with 2 copies (the first copy handles the region inside the bounds,
            // the second handles the region outside of the bounds).
            // We must also extend the source texture by one line to ensure we can wrap on the last line.
            // This is required by the (guest) OpenGL driver.
            if (srcX2 / srcTexture.Info.SamplesInX > srcTexture.Info.Width)
            {
                srcCopyTexture.Height++;

                srcTexture = TextureManager.FindOrCreateTexture(srcCopyTexture, srcTexture.ScaleMode == Image.TextureScaleMode.Scaled);
                if (srcTexture.ScaleFactor != dstTexture.ScaleFactor)
                {
                    srcTexture.PropagateScale(dstTexture);
                }

                srcRegion = new Extents2D(
                    (int)Math.Ceiling(scale * ((srcX1 / srcTexture.Info.SamplesInX) - srcTexture.Info.Width)),
                    (int)Math.Ceiling(scale * ((srcY1 / srcTexture.Info.SamplesInY) + 1)),
                    (int)Math.Ceiling(scale * ((srcX2 / srcTexture.Info.SamplesInX) - srcTexture.Info.Width)),
                    (int)Math.Ceiling(scale * ((srcY2 / srcTexture.Info.SamplesInY) + 1)));

                srcTexture.HostTexture.CopyTo(dstTexture.HostTexture, srcRegion, dstRegion, linearFilter);
            }

            dstTexture.SignalModified();
        }
    }