Apply #595
This commit is contained in:
parent
7c645430d0
commit
edba47b529
6 changed files with 172 additions and 74 deletions
|
@ -3,6 +3,7 @@
|
||||||
public interface IGalPipeline
|
public interface IGalPipeline
|
||||||
{
|
{
|
||||||
void Bind(GalPipelineState state);
|
void Bind(GalPipelineState state);
|
||||||
|
void Unbind(GalPipelineState state);
|
||||||
|
|
||||||
void ResetDepthMask();
|
void ResetDepthMask();
|
||||||
void ResetColorMask(int index);
|
void ResetColorMask(int index);
|
||||||
|
|
|
@ -1,19 +1,23 @@
|
||||||
using OpenTK.Graphics.OpenGL;
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gal.OpenGL
|
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
{
|
{
|
||||||
static class OglExtension
|
static class OglExtension
|
||||||
{
|
{
|
||||||
|
// Private lazy backing variables
|
||||||
private static Lazy<bool> _enhancedLayouts = new Lazy<bool>(() => HasExtension("GL_ARB_enhanced_layouts"));
|
private static Lazy<bool> _enhancedLayouts = new Lazy<bool>(() => HasExtension("GL_ARB_enhanced_layouts"));
|
||||||
private static Lazy<bool> _textureMirrorClamp = new Lazy<bool>(() => HasExtension("GL_EXT_texture_mirror_clamp"));
|
private static Lazy<bool> _textureMirrorClamp = new Lazy<bool>(() => HasExtension("GL_EXT_texture_mirror_clamp"));
|
||||||
private static Lazy<bool> _viewportArray = new Lazy<bool>(() => HasExtension("GL_ARB_viewport_array"));
|
private static Lazy<bool> _viewportArray = new Lazy<bool>(() => HasExtension("GL_ARB_viewport_array"));
|
||||||
|
|
||||||
private static Lazy<bool> _nvidiaDriver = new Lazy<bool>(() => IsNvidiaDriver());
|
private static Lazy<bool> _nvidiaDriver = new Lazy<bool>(() => IsNvidiaDriver());
|
||||||
|
|
||||||
|
// Public accessors
|
||||||
public static bool EnhancedLayouts => _enhancedLayouts.Value;
|
public static bool EnhancedLayouts => _enhancedLayouts.Value;
|
||||||
public static bool TextureMirrorClamp => _textureMirrorClamp.Value;
|
public static bool TextureMirrorClamp => _textureMirrorClamp.Value;
|
||||||
public static bool ViewportArray => _viewportArray.Value;
|
public static bool ViewportArray => _viewportArray.Value;
|
||||||
|
|
||||||
public static bool NvidiaDriver => _nvidiaDriver.Value;
|
public static bool NvidiaDriver => _nvidiaDriver.Value;
|
||||||
|
|
||||||
private static bool HasExtension(string name)
|
private static bool HasExtension(string name)
|
||||||
|
@ -28,11 +32,39 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.PrintInfo(LogClass.Gpu, $"OpenGL extension {name} unavailable. You may experience some performance degradation");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsNvidiaDriver() {
|
private static bool IsNvidiaDriver()
|
||||||
|
{
|
||||||
return GL.GetString(StringName.Vendor).Equals("NVIDIA Corporation");
|
return GL.GetString(StringName.Vendor).Equals("NVIDIA Corporation");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class Required
|
||||||
|
{
|
||||||
|
// Public accessors
|
||||||
|
public static bool EnhancedLayouts => _enhancedLayoutsRequired.Value;
|
||||||
|
public static bool TextureMirrorClamp => _textureMirrorClampRequired.Value;
|
||||||
|
public static bool ViewportArray => _viewportArrayRequired.Value;
|
||||||
|
|
||||||
|
// Private lazy backing variables
|
||||||
|
private static Lazy<bool> _enhancedLayoutsRequired = new Lazy<bool>(() => HasExtensionRequired(OglExtension.EnhancedLayouts, "GL_ARB_enhanced_layouts"));
|
||||||
|
private static Lazy<bool> _textureMirrorClampRequired = new Lazy<bool>(() => HasExtensionRequired(OglExtension.TextureMirrorClamp, "GL_EXT_texture_mirror_clamp"));
|
||||||
|
private static Lazy<bool> _viewportArrayRequired = new Lazy<bool>(() => HasExtensionRequired(OglExtension.ViewportArray, "GL_ARB_viewport_array"));
|
||||||
|
|
||||||
|
private static bool HasExtensionRequired(bool value, string name)
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.PrintWarning(LogClass.Gpu, $"Required OpenGL extension {name} unavailable. You may experience some rendering issues");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -270,47 +270,52 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
|
||||||
|
|
||||||
// Scissor Test
|
// Scissor Test
|
||||||
bool forceUpdate;
|
// All scissor test are disabled before drawing final framebuffer to screen so we don't need to handle disabling
|
||||||
|
// Skip if there are no scissor tests to enable
|
||||||
for (int index = 0; index < New.ScissorTestCount; index++)
|
if (New.ScissorTestCount != 0)
|
||||||
{
|
{
|
||||||
forceUpdate = false;
|
int scissorsApplied = 0;
|
||||||
|
bool applyToAll = false;
|
||||||
|
|
||||||
if (New.ScissorTestEnabled[index])
|
for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++)
|
||||||
{
|
{
|
||||||
// If there is only 1 scissor test, geometry shaders are disabled so the scissor test applies to all viewports
|
if (New.ScissorTestEnabled[index])
|
||||||
if (New.ScissorTestCount == 1)
|
|
||||||
{
|
{
|
||||||
GL.Enable(EnableCap.ScissorTest);
|
// If viewport arrays are unavailable apply first scissor test to all or
|
||||||
}
|
// there is only 1 scissor test and it's the first, the scissor test applies to all viewports
|
||||||
else
|
if (!OglExtension.Required.ViewportArray || (index == 0 && New.ScissorTestCount == 1))
|
||||||
{
|
{
|
||||||
GL.Enable(IndexedEnableCap.ScissorTest, index);
|
GL.Enable(EnableCap.ScissorTest);
|
||||||
}
|
applyToAll = true;
|
||||||
forceUpdate = true;
|
}
|
||||||
}
|
else
|
||||||
else
|
{
|
||||||
{
|
GL.Enable(IndexedEnableCap.ScissorTest, index);
|
||||||
GL.Disable(IndexedEnableCap.ScissorTest, index);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (New.ScissorTestEnabled[index] &&
|
if (New.ScissorTestEnabled[index] != _old.ScissorTestEnabled[index] ||
|
||||||
(New.ScissorTestX[index] != _old.ScissorTestX[index] ||
|
New.ScissorTestX[index] != _old.ScissorTestX[index] ||
|
||||||
New.ScissorTestY[index] != _old.ScissorTestY[index] ||
|
New.ScissorTestY[index] != _old.ScissorTestY[index] ||
|
||||||
New.ScissorTestWidth[index] != _old.ScissorTestWidth[index] ||
|
New.ScissorTestWidth[index] != _old.ScissorTestWidth[index] ||
|
||||||
New.ScissorTestHeight[index] != _old.ScissorTestHeight[index] ||
|
New.ScissorTestHeight[index] != _old.ScissorTestHeight[index])
|
||||||
forceUpdate)) // Force update intentionally last to reduce if comparisons
|
{
|
||||||
{
|
if (applyToAll)
|
||||||
// If there is only 1 scissor test geometry shaders are disables so the scissor test applies to all viewports
|
{
|
||||||
if (New.ScissorTestCount == 1)
|
GL.Scissor(New.ScissorTestX[index], New.ScissorTestY[index],
|
||||||
{
|
New.ScissorTestWidth[index], New.ScissorTestHeight[index]);
|
||||||
GL.Scissor(New.ScissorTestX[index], New.ScissorTestY[index],
|
}
|
||||||
New.ScissorTestWidth[index], New.ScissorTestHeight[index]);
|
else
|
||||||
}
|
{
|
||||||
else
|
GL.ScissorIndexed(index, New.ScissorTestX[index], New.ScissorTestY[index],
|
||||||
{
|
New.ScissorTestWidth[index], New.ScissorTestHeight[index]);
|
||||||
GL.ScissorIndexed(index, New.ScissorTestX[index], New.ScissorTestY[index],
|
}
|
||||||
New.ScissorTestWidth[index], New.ScissorTestHeight[index]);
|
}
|
||||||
|
|
||||||
|
// If all scissor tests have been applied, or viewport arrays are unavailable we can skip remaining iterations
|
||||||
|
if (!OglExtension.Required.ViewportArray || ++scissorsApplied == New.ScissorTestCount)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -378,6 +383,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
_old = New;
|
_old = New;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Unbind(GalPipelineState state)
|
||||||
|
{
|
||||||
|
if (state.ScissorTestCount > 0)
|
||||||
|
{
|
||||||
|
GL.Disable(EnableCap.ScissorTest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void SetAllBlendState(BlendState New)
|
private void SetAllBlendState(BlendState New)
|
||||||
{
|
{
|
||||||
Enable(EnableCap.Blend, New.Enabled);
|
Enable(EnableCap.Blend, New.Enabled);
|
||||||
|
|
|
@ -367,9 +367,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
|
||||||
GL.Disable(EnableCap.FramebufferSrgb);
|
GL.Disable(EnableCap.FramebufferSrgb);
|
||||||
|
|
||||||
// Will be re-enabled if needed while binding, called before any game GL calls
|
|
||||||
GL.Disable(EnableCap.ScissorTest);
|
|
||||||
|
|
||||||
GL.BlitFramebuffer(
|
GL.BlitFramebuffer(
|
||||||
srcX0,
|
srcX0,
|
||||||
srcY0,
|
srcY0,
|
||||||
|
|
|
@ -24,7 +24,12 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
|
|
||||||
private ConstBuffer[][] _constBuffers;
|
private ConstBuffer[][] _constBuffers;
|
||||||
|
|
||||||
// Height kept for flipping y axis
|
// Viewport dimensions kept for scissor test limits
|
||||||
|
private int _viewportX0 = 0;
|
||||||
|
private int _viewportY0 = 0;
|
||||||
|
private int _viewportX1 = 0;
|
||||||
|
private int _viewportY1 = 0;
|
||||||
|
private int _viewportWidth = 0;
|
||||||
private int _viewportHeight = 0;
|
private int _viewportHeight = 0;
|
||||||
|
|
||||||
private int _currentInstance = 0;
|
private int _currentInstance = 0;
|
||||||
|
@ -97,7 +102,14 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
|
|
||||||
GalPipelineState state = new GalPipelineState();
|
GalPipelineState state = new GalPipelineState();
|
||||||
|
|
||||||
|
// Framebuffer must be run configured because viewport dimensions may be used in other methods
|
||||||
SetFrameBuffer(state);
|
SetFrameBuffer(state);
|
||||||
|
|
||||||
|
for (int fbIndex = 0; fbIndex < 8; fbIndex++)
|
||||||
|
{
|
||||||
|
SetFrameBuffer(vmm, fbIndex);
|
||||||
|
}
|
||||||
|
|
||||||
SetFrontFace(state);
|
SetFrontFace(state);
|
||||||
SetCullFace(state);
|
SetCullFace(state);
|
||||||
SetDepth(state);
|
SetDepth(state);
|
||||||
|
@ -107,11 +119,6 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
SetColorMask(state);
|
SetColorMask(state);
|
||||||
SetPrimitiveRestart(state);
|
SetPrimitiveRestart(state);
|
||||||
|
|
||||||
for (int fbIndex = 0; fbIndex < 8; fbIndex++)
|
|
||||||
{
|
|
||||||
SetFrameBuffer(vmm, fbIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
SetZeta(vmm);
|
SetZeta(vmm);
|
||||||
|
|
||||||
SetRenderTargets();
|
SetRenderTargets();
|
||||||
|
@ -206,11 +213,11 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
float sx = ReadRegisterFloat(NvGpuEngine3DReg.ViewportNScaleX + fbIndex * 8);
|
float sx = ReadRegisterFloat(NvGpuEngine3DReg.ViewportNScaleX + fbIndex * 8);
|
||||||
float sy = ReadRegisterFloat(NvGpuEngine3DReg.ViewportNScaleY + fbIndex * 8);
|
float sy = ReadRegisterFloat(NvGpuEngine3DReg.ViewportNScaleY + fbIndex * 8);
|
||||||
|
|
||||||
int vpX = (int)MathF.Max(0, tx - MathF.Abs(sx));
|
_viewportX0 = (int)MathF.Max(0, tx - MathF.Abs(sx));
|
||||||
int vpY = (int)MathF.Max(0, ty - MathF.Abs(sy));
|
_viewportY0 = (int)MathF.Max(0, ty - MathF.Abs(sy));
|
||||||
|
|
||||||
int vpW = (int)(tx + MathF.Abs(sx)) - vpX;
|
_viewportX1 = (int)(tx + MathF.Abs(sx));
|
||||||
int vpH = (int)(ty + MathF.Abs(sy)) - vpY;
|
_viewportY1 = (int)(ty + MathF.Abs(sy));
|
||||||
|
|
||||||
GalImageFormat format = ImageUtils.ConvertSurface((GalSurfaceFormat)surfFormat);
|
GalImageFormat format = ImageUtils.ConvertSurface((GalSurfaceFormat)surfFormat);
|
||||||
|
|
||||||
|
@ -218,9 +225,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
|
|
||||||
_gpu.ResourceManager.SendColorBuffer(vmm, key, fbIndex, image);
|
_gpu.ResourceManager.SendColorBuffer(vmm, key, fbIndex, image);
|
||||||
|
|
||||||
_viewportHeight = vpH;
|
_gpu.Renderer.RenderTarget.SetViewport(fbIndex, _viewportX0, _viewportY0, _viewportX1 - _viewportX0, _viewportY1 - _viewportY0);
|
||||||
|
|
||||||
_gpu.Renderer.RenderTarget.SetViewport(fbIndex, vpX, vpY, vpW, vpH);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetFrameBuffer(GalPipelineState state)
|
private void SetFrameBuffer(GalPipelineState state)
|
||||||
|
@ -422,38 +427,83 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
|
|
||||||
private void SetScissor(GalPipelineState state)
|
private void SetScissor(GalPipelineState state)
|
||||||
{
|
{
|
||||||
// FIXME: Stubbed, only the first scissor test is valid without a geometry shader loaded. At time of writing geometry shaders are also stubbed.
|
int count = 0;
|
||||||
// Once geometry shaders are fixed it should be equal to GalPipelineState.RenderTargetCount when shader loaded, otherwise equal to 1
|
|
||||||
state.ScissorTestCount = 1;
|
|
||||||
|
|
||||||
for (int index = 0; index < state.ScissorTestCount; index++)
|
for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++)
|
||||||
{
|
{
|
||||||
state.ScissorTestEnabled[index] = ReadRegisterBool(NvGpuEngine3DReg.ScissorEnable + index * 4);
|
state.ScissorTestEnabled[index] = ReadRegisterBool(NvGpuEngine3DReg.ScissorEnable + index * 4);
|
||||||
|
|
||||||
if (state.ScissorTestEnabled[index])
|
if (state.ScissorTestEnabled[index])
|
||||||
{
|
{
|
||||||
uint scissorHorizontal = (uint)ReadRegister(NvGpuEngine3DReg.ScissorHorizontal + index * 4);
|
uint scissorHorizontal = (uint)ReadRegister(NvGpuEngine3DReg.ScissorHorizontal + index * 4);
|
||||||
uint scissorVertical = (uint)ReadRegister(NvGpuEngine3DReg.ScissorVertical + index * 4);
|
uint scissorVertical = (uint)ReadRegister(NvGpuEngine3DReg.ScissorVertical + index * 4);
|
||||||
|
|
||||||
state.ScissorTestX[index] = (int)((scissorHorizontal & 0xFFFF) * state.FlipX); // X, lower 16 bits
|
int left = (int)(scissorHorizontal & 0xFFFF); // Left, lower 16 bits
|
||||||
state.ScissorTestWidth[index] = (int)((scissorHorizontal >> 16) * state.FlipX) - state.ScissorTestX[index]; // Width, right side is upper 16 bits
|
int right = (int)(scissorHorizontal >> 16); // Right, upper 16 bits
|
||||||
|
|
||||||
state.ScissorTestY[index] = (int)((scissorVertical & 0xFFFF)); // Y, lower 16 bits
|
int bottom = (int)(scissorVertical & 0xFFFF); // Bottom, lower 16 bits
|
||||||
state.ScissorTestHeight[index] = (int)((scissorVertical >> 16)) - state.ScissorTestY[index]; // Height, top side is upper 16 bits
|
int top = (int)(scissorVertical >> 16); // Top, upper 16 bits
|
||||||
|
|
||||||
// Y coordinates may have to be flipped
|
int width = Math.Abs(right - left);
|
||||||
if ((int)state.FlipY == -1)
|
int height = Math.Abs(top - bottom);
|
||||||
|
|
||||||
|
// If the scissor test covers the whole possible viewport, i.e. uninitialized, disable scissor test
|
||||||
|
if ((width > NvGpu.MaxViewportSize && height > NvGpu.MaxViewportSize) || width <= 0 || height <= 0)
|
||||||
{
|
{
|
||||||
state.ScissorTestY[index] = _viewportHeight - state.ScissorTestY[index] - state.ScissorTestHeight[index];
|
state.ScissorTestEnabled[index] = false;
|
||||||
|
continue;
|
||||||
// Handle negative viewpont coordinate
|
|
||||||
if (state.ScissorTestY[index] < 0)
|
|
||||||
{
|
|
||||||
state.ScissorTestY[index] = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep track of how many scissor tests are active.
|
||||||
|
// If only 1, and it's the first user should apply to all viewports
|
||||||
|
count++;
|
||||||
|
|
||||||
|
// Flip X
|
||||||
|
if (state.FlipX == -1)
|
||||||
|
{
|
||||||
|
left = _viewportX1 - (left - _viewportX0);
|
||||||
|
right = _viewportX1 - (right - _viewportX0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure X is in the right order
|
||||||
|
if (left > right)
|
||||||
|
{
|
||||||
|
int temp = left;
|
||||||
|
left = right;
|
||||||
|
right = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flip Y
|
||||||
|
if (state.FlipY == -1)
|
||||||
|
{
|
||||||
|
bottom = _viewportY1 - (bottom - _viewportY0);
|
||||||
|
top = _viewportY1 - (top - _viewportY0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure Y is in the right order
|
||||||
|
if (bottom > top)
|
||||||
|
{
|
||||||
|
int temp = top;
|
||||||
|
top = bottom;
|
||||||
|
bottom = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle out of active viewport dimensions
|
||||||
|
left = Math.Clamp(left, _viewportX0, _viewportX1);
|
||||||
|
right = Math.Clamp(right, _viewportX0, _viewportX1);
|
||||||
|
top = Math.Clamp(top, _viewportY0, _viewportY1);
|
||||||
|
bottom = Math.Clamp(bottom, _viewportY0, _viewportY1);
|
||||||
|
|
||||||
|
// Save values to state
|
||||||
|
state.ScissorTestX[index] = left;
|
||||||
|
state.ScissorTestY[index] = bottom;
|
||||||
|
|
||||||
|
state.ScissorTestWidth[index] = right - left;
|
||||||
|
state.ScissorTestHeight[index] = top - bottom;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.ScissorTestCount = count;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetBlending(GalPipelineState state)
|
private void SetBlending(GalPipelineState state)
|
||||||
|
@ -541,7 +591,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
private void SetRenderTargets()
|
private void SetRenderTargets()
|
||||||
{
|
{
|
||||||
//Commercial games do not seem to
|
//Commercial games do not seem to
|
||||||
//bool SeparateFragData = ReadRegisterBool(NvGpuEngine3dReg.RTSeparateFragData);
|
//bool SeparateFragData = ReadRegisterBool(NvGpuEngine3DReg.RTSeparateFragData);
|
||||||
|
|
||||||
uint control = (uint)(ReadRegister(NvGpuEngine3DReg.RtControl));
|
uint control = (uint)(ReadRegister(NvGpuEngine3DReg.RtControl));
|
||||||
|
|
||||||
|
@ -996,6 +1046,9 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
_gpu.Renderer.Rasterizer.DrawArrays(vertexFirst, vertexCount, primType);
|
_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?
|
//Is the GPU really clearing those registers after draw?
|
||||||
WriteRegister(NvGpuEngine3DReg.IndexBatchFirst, 0);
|
WriteRegister(NvGpuEngine3DReg.IndexBatchFirst, 0);
|
||||||
WriteRegister(NvGpuEngine3DReg.IndexBatchCount, 0);
|
WriteRegister(NvGpuEngine3DReg.IndexBatchCount, 0);
|
||||||
|
|
|
@ -8,6 +8,8 @@ namespace Ryujinx.Graphics
|
||||||
{
|
{
|
||||||
public class NvGpu
|
public class NvGpu
|
||||||
{
|
{
|
||||||
|
public const int MaxViewportSize = 0x3FFF;
|
||||||
|
|
||||||
public IGalRenderer Renderer { get; private set; }
|
public IGalRenderer Renderer { get; private set; }
|
||||||
|
|
||||||
public GpuResourceManager ResourceManager { get; private set; }
|
public GpuResourceManager ResourceManager { get; private set; }
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue