2019-10-13 08:02:07 +02:00
using OpenTK.Graphics.OpenGL ;
2019-12-29 00:45:33 +01:00
using Ryujinx.Common.Logging ;
2019-10-13 08:02:07 +02:00
using Ryujinx.Graphics.GAL ;
2020-05-23 11:46:09 +02:00
using Ryujinx.Graphics.OpenGL.Image ;
2020-05-04 04:24:59 +02:00
using Ryujinx.Graphics.OpenGL.Queries ;
2019-10-13 08:02:07 +02:00
using Ryujinx.Graphics.Shader ;
using System ;
namespace Ryujinx.Graphics.OpenGL
{
2019-12-31 23:09:49 +01:00
class Pipeline : IPipeline , IDisposable
2019-10-13 08:02:07 +02:00
{
private Program _program ;
2020-04-07 11:19:45 +02:00
private bool _rasterizerDiscard ;
2019-10-13 08:02:07 +02:00
private VertexArray _vertexArray ;
private Framebuffer _framebuffer ;
private IntPtr _indexBaseOffset ;
private DrawElementsType _elementsType ;
private PrimitiveType _primitiveType ;
2020-03-29 14:48:39 +02:00
private int _stencilFrontMask ;
2019-10-13 08:02:07 +02:00
private bool _depthMask ;
private bool _depthTest ;
private bool _hasDepthBuffer ;
2020-05-24 15:44:12 +02:00
private int _boundDrawFramebuffer ;
private int _boundReadFramebuffer ;
2020-07-07 04:41:07 +02:00
private float [ ] _fpRenderScale = new float [ 33 ] ;
private float [ ] _cpRenderScale = new float [ 32 ] ;
2020-04-25 15:02:18 +02:00
private TextureBase _unit0Texture ;
2020-07-07 04:41:07 +02:00
private TextureBase _rtColor0Texture ;
private TextureBase _rtDepthTexture ;
2019-10-13 08:02:07 +02:00
2020-03-29 14:48:39 +02:00
private ClipOrigin _clipOrigin ;
2019-12-07 05:54:28 +01:00
private ClipDepthMode _clipDepthMode ;
2019-10-13 08:02:07 +02:00
2020-05-23 11:46:09 +02:00
private readonly uint [ ] _componentMasks ;
2019-10-13 08:02:07 +02:00
2020-04-07 11:19:45 +02:00
private bool _scissor0Enable = false ;
2020-03-29 05:02:58 +02:00
2020-04-25 15:00:43 +02:00
ColorF _blendConstant = new ColorF ( 0 , 0 , 0 , 0 ) ;
2019-10-18 04:41:18 +02:00
internal Pipeline ( )
2019-10-13 08:02:07 +02:00
{
2020-04-07 11:19:45 +02:00
_rasterizerDiscard = false ;
2020-03-29 14:48:39 +02:00
_clipOrigin = ClipOrigin . LowerLeft ;
2019-12-07 05:54:28 +01:00
_clipDepthMode = ClipDepthMode . NegativeOneToOne ;
2020-05-23 11:46:09 +02:00
_componentMasks = new uint [ Constants . MaxRenderTargets ] ;
for ( int index = 0 ; index < Constants . MaxRenderTargets ; index + + )
{
_componentMasks [ index ] = 0xf ;
}
2020-07-07 04:41:07 +02:00
for ( int index = 0 ; index < _fpRenderScale . Length ; index + + )
{
_fpRenderScale [ index ] = 1f ;
}
for ( int index = 0 ; index < _cpRenderScale . Length ; index + + )
{
_cpRenderScale [ index ] = 1f ;
}
2019-10-13 08:02:07 +02:00
}
2020-01-12 23:12:40 +01:00
public void Barrier ( )
{
GL . MemoryBarrier ( MemoryBarrierFlags . AllBarrierBits ) ;
}
2019-10-13 08:02:07 +02:00
public void ClearRenderTargetColor ( int index , uint componentMask , ColorF color )
{
GL . ColorMask (
index ,
( componentMask & 1 ) ! = 0 ,
( componentMask & 2 ) ! = 0 ,
( componentMask & 4 ) ! = 0 ,
( componentMask & 8 ) ! = 0 ) ;
float [ ] colors = new float [ ] { color . Red , color . Green , color . Blue , color . Alpha } ;
GL . ClearBuffer ( ClearBuffer . Color , index , colors ) ;
RestoreComponentMask ( index ) ;
2020-03-29 14:48:39 +02:00
_framebuffer . SignalModified ( ) ;
2019-10-13 08:02:07 +02:00
}
2019-12-29 18:41:50 +01:00
public void ClearRenderTargetDepthStencil ( float depthValue , bool depthMask , int stencilValue , int stencilMask )
2019-10-13 08:02:07 +02:00
{
bool stencilMaskChanged =
stencilMask ! = 0 & &
stencilMask ! = _stencilFrontMask ;
bool depthMaskChanged = depthMask & & depthMask ! = _depthMask ;
if ( stencilMaskChanged )
{
GL . StencilMaskSeparate ( StencilFace . Front , stencilMask ) ;
}
if ( depthMaskChanged )
{
GL . DepthMask ( depthMask ) ;
}
if ( depthMask & & stencilMask ! = 0 )
{
GL . ClearBuffer ( ClearBufferCombined . DepthStencil , 0 , depthValue , stencilValue ) ;
}
else if ( depthMask )
{
GL . ClearBuffer ( ClearBuffer . Depth , 0 , ref depthValue ) ;
}
else if ( stencilMask ! = 0 )
{
GL . ClearBuffer ( ClearBuffer . Stencil , 0 , ref stencilValue ) ;
}
if ( stencilMaskChanged )
{
GL . StencilMaskSeparate ( StencilFace . Front , _stencilFrontMask ) ;
}
if ( depthMaskChanged )
{
GL . DepthMask ( _depthMask ) ;
}
2020-03-29 14:48:39 +02:00
_framebuffer . SignalModified ( ) ;
2019-10-13 08:02:07 +02:00
}
2020-05-23 11:46:09 +02:00
public void CopyBuffer ( BufferHandle source , BufferHandle destination , int srcOffset , int dstOffset , int size )
{
Buffer . Copy ( source , destination , srcOffset , dstOffset , size ) ;
}
2019-12-29 18:41:50 +01:00
public void DispatchCompute ( int groupsX , int groupsY , int groupsZ )
2019-10-18 04:41:18 +02:00
{
2019-11-27 04:41:22 +01:00
if ( ! _program . IsLinked )
{
2019-12-30 18:47:20 +01:00
Logger . PrintDebug ( LogClass . Gpu , "Dispatch error, shader not linked." ) ;
2019-12-29 00:45:33 +01:00
2019-11-27 04:41:22 +01:00
return ;
}
PrepareForDispatch ( ) ;
2019-10-18 04:41:18 +02:00
GL . DispatchCompute ( groupsX , groupsY , groupsZ ) ;
}
2019-10-13 08:02:07 +02:00
public void Draw ( int vertexCount , int instanceCount , int firstVertex , int firstInstance )
{
if ( ! _program . IsLinked )
{
2019-12-30 18:47:20 +01:00
Logger . PrintDebug ( LogClass . Gpu , "Draw error, shader not linked." ) ;
2019-12-29 00:45:33 +01:00
2019-10-13 08:02:07 +02:00
return ;
}
PrepareForDraw ( ) ;
2019-10-15 05:10:20 +02:00
if ( _primitiveType = = PrimitiveType . Quads )
2019-10-13 08:02:07 +02:00
{
2019-10-15 05:10:20 +02:00
DrawQuadsImpl ( vertexCount , instanceCount , firstVertex , firstInstance ) ;
}
else if ( _primitiveType = = PrimitiveType . QuadStrip )
{
DrawQuadStripImpl ( vertexCount , instanceCount , firstVertex , firstInstance ) ;
}
else
{
DrawImpl ( vertexCount , instanceCount , firstVertex , firstInstance ) ;
}
2020-03-29 14:48:39 +02:00
_framebuffer . SignalModified ( ) ;
2019-10-15 05:10:20 +02:00
}
2019-10-13 08:02:07 +02:00
2019-10-15 05:10:20 +02:00
private void DrawQuadsImpl (
int vertexCount ,
int instanceCount ,
int firstVertex ,
int firstInstance )
{
// TODO: Instanced rendering.
int quadsCount = vertexCount / 4 ;
int [ ] firsts = new int [ quadsCount ] ;
int [ ] counts = new int [ quadsCount ] ;
for ( int quadIndex = 0 ; quadIndex < quadsCount ; quadIndex + + )
{
firsts [ quadIndex ] = firstVertex + quadIndex * 4 ;
counts [ quadIndex ] = 4 ;
}
GL . MultiDrawArrays (
PrimitiveType . TriangleFan ,
firsts ,
counts ,
quadsCount ) ;
}
private void DrawQuadStripImpl (
int vertexCount ,
int instanceCount ,
int firstVertex ,
int firstInstance )
{
int quadsCount = ( vertexCount - 2 ) / 2 ;
2020-01-12 23:14:50 +01:00
if ( firstInstance ! = 0 | | instanceCount ! = 1 )
2019-10-15 05:10:20 +02:00
{
2020-01-12 23:14:50 +01:00
for ( int quadIndex = 0 ; quadIndex < quadsCount ; quadIndex + + )
{
GL . DrawArraysInstancedBaseInstance ( PrimitiveType . TriangleFan , firstVertex + quadIndex * 2 , 4 , instanceCount , firstInstance ) ;
}
}
else
{
int [ ] firsts = new int [ quadsCount ] ;
int [ ] counts = new int [ quadsCount ] ;
firsts [ 0 ] = firstVertex ;
counts [ 0 ] = 4 ;
for ( int quadIndex = 1 ; quadIndex < quadsCount ; quadIndex + + )
{
firsts [ quadIndex ] = firstVertex + quadIndex * 2 ;
counts [ quadIndex ] = 4 ;
}
GL . MultiDrawArrays (
PrimitiveType . TriangleFan ,
firsts ,
counts ,
quadsCount ) ;
2019-10-15 05:10:20 +02:00
}
}
2019-10-13 08:02:07 +02:00
2019-10-15 05:10:20 +02:00
private void DrawImpl (
int vertexCount ,
int instanceCount ,
int firstVertex ,
int firstInstance )
{
if ( firstInstance = = 0 & & instanceCount = = 1 )
{
GL . DrawArrays ( _primitiveType , firstVertex , vertexCount ) ;
2019-10-13 08:02:07 +02:00
}
else if ( firstInstance = = 0 )
{
GL . DrawArraysInstanced ( _primitiveType , firstVertex , vertexCount , instanceCount ) ;
}
else
{
GL . DrawArraysInstancedBaseInstance (
_primitiveType ,
firstVertex ,
vertexCount ,
instanceCount ,
firstInstance ) ;
}
}
public void DrawIndexed (
int indexCount ,
int instanceCount ,
int firstIndex ,
int firstVertex ,
int firstInstance )
{
if ( ! _program . IsLinked )
{
2019-12-30 18:47:20 +01:00
Logger . PrintDebug ( LogClass . Gpu , "Draw error, shader not linked." ) ;
2019-12-29 00:45:33 +01:00
2019-10-13 08:02:07 +02:00
return ;
}
PrepareForDraw ( ) ;
2019-10-15 05:10:20 +02:00
int indexElemSize = 1 ;
2019-10-13 08:02:07 +02:00
switch ( _elementsType )
{
2019-10-15 05:10:20 +02:00
case DrawElementsType . UnsignedShort : indexElemSize = 2 ; break ;
2020-03-29 14:48:39 +02:00
case DrawElementsType . UnsignedInt : indexElemSize = 4 ; break ;
2019-10-15 05:10:20 +02:00
}
IntPtr indexBaseOffset = _indexBaseOffset + firstIndex * indexElemSize ;
if ( _primitiveType = = PrimitiveType . Quads )
{
DrawQuadsIndexedImpl (
indexCount ,
instanceCount ,
indexBaseOffset ,
indexElemSize ,
firstVertex ,
firstInstance ) ;
}
else if ( _primitiveType = = PrimitiveType . QuadStrip )
{
DrawQuadStripIndexedImpl (
indexCount ,
instanceCount ,
indexBaseOffset ,
indexElemSize ,
firstVertex ,
firstInstance ) ;
}
else
{
DrawIndexedImpl (
indexCount ,
instanceCount ,
indexBaseOffset ,
firstVertex ,
firstInstance ) ;
}
2020-03-29 14:48:39 +02:00
_framebuffer . SignalModified ( ) ;
2019-10-15 05:10:20 +02:00
}
private void DrawQuadsIndexedImpl (
2020-03-29 14:48:39 +02:00
int indexCount ,
int instanceCount ,
2019-10-15 05:10:20 +02:00
IntPtr indexBaseOffset ,
2020-03-29 14:48:39 +02:00
int indexElemSize ,
int firstVertex ,
int firstInstance )
2019-10-15 05:10:20 +02:00
{
int quadsCount = indexCount / 4 ;
2020-01-12 23:14:50 +01:00
if ( firstInstance ! = 0 | | instanceCount ! = 1 )
{
if ( firstVertex ! = 0 & & firstInstance ! = 0 )
{
for ( int quadIndex = 0 ; quadIndex < quadsCount ; quadIndex + + )
{
GL . DrawElementsInstancedBaseVertexBaseInstance (
PrimitiveType . TriangleFan ,
4 ,
_elementsType ,
indexBaseOffset + quadIndex * 4 * indexElemSize ,
instanceCount ,
firstVertex ,
firstInstance ) ;
}
}
else if ( firstInstance ! = 0 )
{
for ( int quadIndex = 0 ; quadIndex < quadsCount ; quadIndex + + )
{
GL . DrawElementsInstancedBaseInstance (
PrimitiveType . TriangleFan ,
4 ,
_elementsType ,
indexBaseOffset + quadIndex * 4 * indexElemSize ,
instanceCount ,
firstInstance ) ;
}
}
else
{
for ( int quadIndex = 0 ; quadIndex < quadsCount ; quadIndex + + )
{
GL . DrawElementsInstanced (
PrimitiveType . TriangleFan ,
4 ,
_elementsType ,
indexBaseOffset + quadIndex * 4 * indexElemSize ,
instanceCount ) ;
}
}
}
else
{
IntPtr [ ] indices = new IntPtr [ quadsCount ] ;
2019-10-15 05:10:20 +02:00
2020-01-12 23:14:50 +01:00
int [ ] counts = new int [ quadsCount ] ;
2019-10-15 05:10:20 +02:00
2020-01-12 23:14:50 +01:00
int [ ] baseVertices = new int [ quadsCount ] ;
2019-10-15 05:10:20 +02:00
2020-01-12 23:14:50 +01:00
for ( int quadIndex = 0 ; quadIndex < quadsCount ; quadIndex + + )
{
indices [ quadIndex ] = indexBaseOffset + quadIndex * 4 * indexElemSize ;
2019-10-15 05:10:20 +02:00
2020-01-12 23:14:50 +01:00
counts [ quadIndex ] = 4 ;
2019-10-15 05:10:20 +02:00
2020-01-12 23:14:50 +01:00
baseVertices [ quadIndex ] = firstVertex ;
}
2019-10-15 05:10:20 +02:00
2020-01-12 23:14:50 +01:00
GL . MultiDrawElementsBaseVertex (
PrimitiveType . TriangleFan ,
counts ,
_elementsType ,
indices ,
quadsCount ,
baseVertices ) ;
}
2019-10-15 05:10:20 +02:00
}
private void DrawQuadStripIndexedImpl (
2020-03-29 14:48:39 +02:00
int indexCount ,
int instanceCount ,
2019-10-15 05:10:20 +02:00
IntPtr indexBaseOffset ,
2020-03-29 14:48:39 +02:00
int indexElemSize ,
int firstVertex ,
int firstInstance )
2019-10-15 05:10:20 +02:00
{
// TODO: Instanced rendering.
int quadsCount = ( indexCount - 2 ) / 2 ;
IntPtr [ ] indices = new IntPtr [ quadsCount ] ;
int [ ] counts = new int [ quadsCount ] ;
int [ ] baseVertices = new int [ quadsCount ] ;
indices [ 0 ] = indexBaseOffset ;
counts [ 0 ] = 4 ;
baseVertices [ 0 ] = firstVertex ;
for ( int quadIndex = 1 ; quadIndex < quadsCount ; quadIndex + + )
{
indices [ quadIndex ] = indexBaseOffset + quadIndex * 2 * indexElemSize ;
counts [ quadIndex ] = 4 ;
baseVertices [ quadIndex ] = firstVertex ;
2019-10-13 08:02:07 +02:00
}
2019-10-15 05:10:20 +02:00
GL . MultiDrawElementsBaseVertex (
PrimitiveType . TriangleFan ,
counts ,
_elementsType ,
indices ,
quadsCount ,
baseVertices ) ;
}
2019-10-13 08:02:07 +02:00
2019-10-15 05:10:20 +02:00
private void DrawIndexedImpl (
2020-03-29 14:48:39 +02:00
int indexCount ,
int instanceCount ,
2019-10-15 05:10:20 +02:00
IntPtr indexBaseOffset ,
2020-03-29 14:48:39 +02:00
int firstVertex ,
int firstInstance )
2019-10-15 05:10:20 +02:00
{
2019-10-13 08:02:07 +02:00
if ( firstInstance = = 0 & & firstVertex = = 0 & & instanceCount = = 1 )
{
GL . DrawElements ( _primitiveType , indexCount , _elementsType , indexBaseOffset ) ;
}
else if ( firstInstance = = 0 & & instanceCount = = 1 )
{
GL . DrawElementsBaseVertex (
_primitiveType ,
indexCount ,
_elementsType ,
indexBaseOffset ,
firstVertex ) ;
}
else if ( firstInstance = = 0 & & firstVertex = = 0 )
{
GL . DrawElementsInstanced (
_primitiveType ,
indexCount ,
_elementsType ,
indexBaseOffset ,
instanceCount ) ;
}
else if ( firstInstance = = 0 )
{
GL . DrawElementsInstancedBaseVertex (
_primitiveType ,
indexCount ,
_elementsType ,
indexBaseOffset ,
instanceCount ,
firstVertex ) ;
}
else if ( firstVertex = = 0 )
{
GL . DrawElementsInstancedBaseInstance (
_primitiveType ,
indexCount ,
_elementsType ,
indexBaseOffset ,
instanceCount ,
firstInstance ) ;
}
else
{
GL . DrawElementsInstancedBaseVertexBaseInstance (
_primitiveType ,
indexCount ,
_elementsType ,
indexBaseOffset ,
instanceCount ,
firstVertex ,
firstInstance ) ;
}
}
2019-12-29 18:41:50 +01:00
public void SetBlendState ( int index , BlendDescriptor blend )
2019-10-13 08:02:07 +02:00
{
2019-12-29 18:41:50 +01:00
if ( ! blend . Enable )
{
GL . Disable ( IndexedEnableCap . Blend , index ) ;
2019-10-13 08:02:07 +02:00
2019-12-29 18:41:50 +01:00
return ;
}
2019-10-13 08:02:07 +02:00
2019-12-29 18:41:50 +01:00
GL . BlendEquationSeparate (
index ,
blend . ColorOp . Convert ( ) ,
blend . AlphaOp . Convert ( ) ) ;
GL . BlendFuncSeparate (
index ,
( BlendingFactorSrc ) blend . ColorSrcFactor . Convert ( ) ,
( BlendingFactorDest ) blend . ColorDstFactor . Convert ( ) ,
( BlendingFactorSrc ) blend . AlphaSrcFactor . Convert ( ) ,
( BlendingFactorDest ) blend . AlphaDstFactor . Convert ( ) ) ;
2020-04-25 15:00:43 +02:00
if ( _blendConstant ! = blend . BlendConstant )
{
_blendConstant = blend . BlendConstant ;
GL . BlendColor (
blend . BlendConstant . Red ,
blend . BlendConstant . Green ,
blend . BlendConstant . Blue ,
blend . BlendConstant . Alpha ) ;
}
2019-12-29 18:41:50 +01:00
GL . Enable ( IndexedEnableCap . Blend , index ) ;
2019-10-13 08:02:07 +02:00
}
2020-07-10 19:23:15 +02:00
public void SetLogicOpState ( bool enable , LogicalOp op )
{
if ( enable )
{
GL . Enable ( EnableCap . ColorLogicOp ) ;
GL . LogicOp ( ( LogicOp ) op . Convert ( ) ) ;
}
else
{
GL . Disable ( EnableCap . ColorLogicOp ) ;
}
}
2019-10-13 08:02:07 +02:00
public void SetDepthBias ( PolygonModeMask enables , float factor , float units , float clamp )
{
if ( ( enables & PolygonModeMask . Point ) ! = 0 )
{
GL . Enable ( EnableCap . PolygonOffsetPoint ) ;
}
else
{
GL . Disable ( EnableCap . PolygonOffsetPoint ) ;
}
if ( ( enables & PolygonModeMask . Line ) ! = 0 )
{
GL . Enable ( EnableCap . PolygonOffsetLine ) ;
}
else
{
GL . Disable ( EnableCap . PolygonOffsetLine ) ;
}
if ( ( enables & PolygonModeMask . Fill ) ! = 0 )
{
GL . Enable ( EnableCap . PolygonOffsetFill ) ;
}
else
{
GL . Disable ( EnableCap . PolygonOffsetFill ) ;
}
if ( enables = = 0 )
{
return ;
}
2020-04-30 03:47:24 +02:00
GL . PolygonOffset ( factor , units / 2f ) ;
2020-01-06 02:04:37 +01:00
// TODO: Enable when GL_EXT_polygon_offset_clamp is supported.
2019-10-13 08:02:07 +02:00
// GL.PolygonOffsetClamp(factor, units, clamp);
}
2020-04-30 03:47:24 +02:00
public void SetDepthClamp ( bool clamp )
2020-04-17 03:16:49 +02:00
{
if ( ! clamp )
{
GL . Disable ( EnableCap . DepthClamp ) ;
return ;
}
GL . Enable ( EnableCap . DepthClamp ) ;
}
2019-12-07 05:54:28 +01:00
public void SetDepthMode ( DepthMode mode )
{
ClipDepthMode depthMode = mode . Convert ( ) ;
if ( _clipDepthMode ! = depthMode )
{
_clipDepthMode = depthMode ;
GL . ClipControl ( _clipOrigin , depthMode ) ;
}
}
2019-10-13 08:02:07 +02:00
public void SetDepthTest ( DepthTestDescriptor depthTest )
{
GL . DepthFunc ( ( DepthFunction ) depthTest . Func . Convert ( ) ) ;
_depthMask = depthTest . WriteEnable ;
_depthTest = depthTest . TestEnable ;
UpdateDepthTest ( ) ;
}
public void SetFaceCulling ( bool enable , Face face )
{
if ( ! enable )
{
GL . Disable ( EnableCap . CullFace ) ;
return ;
}
GL . CullFace ( face . Convert ( ) ) ;
GL . Enable ( EnableCap . CullFace ) ;
}
public void SetFrontFace ( FrontFace frontFace )
{
GL . FrontFace ( frontFace . Convert ( ) ) ;
}
2019-12-29 18:41:50 +01:00
public void SetImage ( int index , ShaderStage stage , ITexture texture )
{
int unit = _program . GetImageUnit ( stage , index ) ;
if ( unit ! = - 1 & & texture ! = null )
{
2020-04-25 15:02:18 +02:00
TextureBase texBase = ( TextureBase ) texture ;
2019-12-29 18:41:50 +01:00
2020-04-25 15:02:18 +02:00
FormatInfo formatInfo = FormatTable . GetFormatInfo ( texBase . Format ) ;
2019-12-29 18:41:50 +01:00
SizedInternalFormat format = ( SizedInternalFormat ) formatInfo . PixelInternalFormat ;
2020-04-25 15:02:18 +02:00
GL . BindImageTexture ( unit , texBase . Handle , 0 , true , 0 , TextureAccess . ReadWrite , format ) ;
2019-12-29 18:41:50 +01:00
}
}
public void SetIndexBuffer ( BufferRange buffer , IndexType type )
{
_elementsType = type . Convert ( ) ;
_indexBaseOffset = ( IntPtr ) buffer . Offset ;
EnsureVertexArray ( ) ;
2020-05-23 11:46:09 +02:00
_vertexArray . SetIndexBuffer ( buffer . Handle ) ;
2019-12-29 18:41:50 +01:00
}
2020-05-28 01:03:07 +02:00
public void SetOrigin ( Origin origin )
{
ClipOrigin clipOrigin = origin = = Origin . UpperLeft ? ClipOrigin . UpperLeft : ClipOrigin . LowerLeft ;
SetOrigin ( clipOrigin ) ;
}
2020-02-02 00:19:46 +01:00
public void SetPointSize ( float size )
{
GL . PointSize ( size ) ;
}
2019-10-13 08:02:07 +02:00
public void SetPrimitiveRestart ( bool enable , int index )
{
if ( ! enable )
{
GL . Disable ( EnableCap . PrimitiveRestart ) ;
return ;
}
GL . PrimitiveRestartIndex ( index ) ;
GL . Enable ( EnableCap . PrimitiveRestart ) ;
}
public void SetPrimitiveTopology ( PrimitiveTopology topology )
{
_primitiveType = topology . Convert ( ) ;
}
2019-12-29 18:41:50 +01:00
public void SetProgram ( IProgram program )
{
_program = ( Program ) program ;
_program . Bind ( ) ;
2020-07-07 04:41:07 +02:00
SetRenderTargetScale ( _fpRenderScale [ 0 ] ) ;
2019-12-29 18:41:50 +01:00
}
2020-04-07 11:19:45 +02:00
public void SetRasterizerDiscard ( bool discard )
{
if ( discard )
{
GL . Enable ( EnableCap . RasterizerDiscard ) ;
}
else
{
GL . Disable ( EnableCap . RasterizerDiscard ) ;
}
_rasterizerDiscard = discard ;
}
2020-07-07 04:41:07 +02:00
public void SetRenderTargetScale ( float scale )
{
_fpRenderScale [ 0 ] = scale ;
if ( _program ! = null & & _program . FragmentRenderScaleUniform ! = - 1 )
{
GL . Uniform1 ( _program . FragmentRenderScaleUniform , 1 , _fpRenderScale ) ; // Just the first element.
}
}
2020-05-23 11:46:09 +02:00
public void SetRenderTargetColorMasks ( ReadOnlySpan < uint > componentMasks )
2019-10-13 08:02:07 +02:00
{
for ( int index = 0 ; index < componentMasks . Length ; index + + )
{
2020-05-23 11:46:09 +02:00
_componentMasks [ index ] = componentMasks [ index ] ;
2019-10-13 08:02:07 +02:00
RestoreComponentMask ( index ) ;
}
}
public void SetRenderTargets ( ITexture [ ] colors , ITexture depthStencil )
{
EnsureFramebuffer ( ) ;
2020-07-07 04:41:07 +02:00
_rtColor0Texture = ( TextureBase ) colors [ 0 ] ;
_rtDepthTexture = ( TextureBase ) depthStencil ;
2019-10-13 08:02:07 +02:00
for ( int index = 0 ; index < colors . Length ; index + + )
{
TextureView color = ( TextureView ) colors [ index ] ;
_framebuffer . AttachColor ( index , color ) ;
}
TextureView depthStencilView = ( TextureView ) depthStencil ;
_framebuffer . AttachDepthStencil ( depthStencilView ) ;
_framebuffer . SetDrawBuffers ( colors . Length ) ;
_hasDepthBuffer = depthStencil ! = null & & depthStencilView . Format ! = Format . S8Uint ;
UpdateDepthTest ( ) ;
}
2019-12-29 18:41:50 +01:00
public void SetSampler ( int index , ShaderStage stage , ISampler sampler )
{
int unit = _program . GetTextureUnit ( stage , index ) ;
if ( unit ! = - 1 & & sampler ! = null )
{
( ( Sampler ) sampler ) . Bind ( unit ) ;
}
}
2020-03-29 05:02:58 +02:00
public void SetScissorEnable ( int index , bool enable )
{
if ( enable )
{
GL . Enable ( IndexedEnableCap . ScissorTest , index ) ;
}
else
{
GL . Disable ( IndexedEnableCap . ScissorTest , index ) ;
}
2020-04-07 11:19:45 +02:00
if ( index = = 0 )
{
_scissor0Enable = enable ;
}
2020-03-29 05:02:58 +02:00
}
public void SetScissor ( int index , int x , int y , int width , int height )
{
GL . ScissorIndexed ( index , x , y , width , height ) ;
}
2019-10-13 08:02:07 +02:00
public void SetStencilTest ( StencilTestDescriptor stencilTest )
{
if ( ! stencilTest . TestEnable )
{
GL . Disable ( EnableCap . StencilTest ) ;
return ;
}
GL . StencilOpSeparate (
StencilFace . Front ,
stencilTest . FrontSFail . Convert ( ) ,
stencilTest . FrontDpFail . Convert ( ) ,
stencilTest . FrontDpPass . Convert ( ) ) ;
GL . StencilFuncSeparate (
StencilFace . Front ,
( StencilFunction ) stencilTest . FrontFunc . Convert ( ) ,
stencilTest . FrontFuncRef ,
stencilTest . FrontFuncMask ) ;
GL . StencilMaskSeparate ( StencilFace . Front , stencilTest . FrontMask ) ;
GL . StencilOpSeparate (
StencilFace . Back ,
stencilTest . BackSFail . Convert ( ) ,
stencilTest . BackDpFail . Convert ( ) ,
stencilTest . BackDpPass . Convert ( ) ) ;
GL . StencilFuncSeparate (
StencilFace . Back ,
( StencilFunction ) stencilTest . BackFunc . Convert ( ) ,
stencilTest . BackFuncRef ,
stencilTest . BackFuncMask ) ;
GL . StencilMaskSeparate ( StencilFace . Back , stencilTest . BackMask ) ;
GL . Enable ( EnableCap . StencilTest ) ;
_stencilFrontMask = stencilTest . FrontMask ;
}
2019-12-29 18:41:50 +01:00
public void SetStorageBuffer ( int index , ShaderStage stage , BufferRange buffer )
{
SetBuffer ( index , stage , buffer , isStorage : true ) ;
}
public void SetTexture ( int index , ShaderStage stage , ITexture texture )
{
int unit = _program . GetTextureUnit ( stage , index ) ;
if ( unit ! = - 1 & & texture ! = null )
{
if ( unit = = 0 )
{
2020-04-25 15:02:18 +02:00
_unit0Texture = ( TextureBase ) texture ;
2019-12-29 18:41:50 +01:00
}
else
{
2020-04-25 15:02:18 +02:00
( ( TextureBase ) texture ) . Bind ( unit ) ;
2019-12-29 18:41:50 +01:00
}
2020-07-07 04:41:07 +02:00
// Update scale factor for bound textures.
switch ( stage )
{
case ShaderStage . Fragment :
if ( _program . FragmentRenderScaleUniform ! = - 1 )
{
// Only update and send sampled texture scales if the shader uses them.
bool interpolate = false ;
float scale = texture . ScaleFactor ;
if ( scale ! = 1 )
{
TextureBase activeTarget = _rtColor0Texture ? ? _rtDepthTexture ;
if ( activeTarget ! = null & & activeTarget . Width / ( float ) texture . Width = = activeTarget . Height / ( float ) texture . Height )
{
// If the texture's size is a multiple of the sampler size, enable interpolation using gl_FragCoord. (helps "invent" new integer values between scaled pixels)
interpolate = true ;
}
}
_fpRenderScale [ index + 1 ] = interpolate ? - scale : scale ;
}
break ;
case ShaderStage . Compute :
_cpRenderScale [ index ] = texture . ScaleFactor ;
break ;
}
2019-12-29 18:41:50 +01:00
}
}
public void SetUniformBuffer ( int index , ShaderStage stage , BufferRange buffer )
{
SetBuffer ( index , stage , buffer , isStorage : false ) ;
}
2020-05-04 04:04:49 +02:00
public void SetUserClipDistance ( int index , bool enableClip )
{
if ( ! enableClip )
{
GL . Disable ( EnableCap . ClipDistance0 + index ) ;
return ;
}
GL . Enable ( EnableCap . ClipDistance0 + index ) ;
}
2020-05-23 11:46:09 +02:00
public void SetVertexAttribs ( ReadOnlySpan < VertexAttribDescriptor > vertexAttribs )
2019-12-29 18:41:50 +01:00
{
EnsureVertexArray ( ) ;
_vertexArray . SetVertexAttributes ( vertexAttribs ) ;
}
2020-05-23 11:46:09 +02:00
public void SetVertexBuffers ( ReadOnlySpan < VertexBufferDescriptor > vertexBuffers )
2019-12-29 18:41:50 +01:00
{
EnsureVertexArray ( ) ;
_vertexArray . SetVertexBuffers ( vertexBuffers ) ;
}
2020-05-23 11:46:09 +02:00
public void SetViewports ( int first , ReadOnlySpan < Viewport > viewports )
2019-10-13 08:02:07 +02:00
{
float [ ] viewportArray = new float [ viewports . Length * 4 ] ;
double [ ] depthRangeArray = new double [ viewports . Length * 2 ] ;
for ( int index = 0 ; index < viewports . Length ; index + + )
{
int viewportElemIndex = index * 4 ;
Viewport viewport = viewports [ index ] ;
viewportArray [ viewportElemIndex + 0 ] = viewport . Region . X ;
viewportArray [ viewportElemIndex + 1 ] = viewport . Region . Y ;
2020-05-28 01:03:07 +02:00
if ( HwCapabilities . SupportsViewportSwizzle )
2019-10-13 08:02:07 +02:00
{
2020-05-28 01:03:07 +02:00
GL . NV . ViewportSwizzle (
index ,
viewport . SwizzleX . Convert ( ) ,
viewport . SwizzleY . Convert ( ) ,
viewport . SwizzleZ . Convert ( ) ,
viewport . SwizzleW . Convert ( ) ) ;
2019-10-13 08:02:07 +02:00
}
viewportArray [ viewportElemIndex + 2 ] = MathF . Abs ( viewport . Region . Width ) ;
viewportArray [ viewportElemIndex + 3 ] = MathF . Abs ( viewport . Region . Height ) ;
depthRangeArray [ index * 2 + 0 ] = viewport . DepthNear ;
depthRangeArray [ index * 2 + 1 ] = viewport . DepthFar ;
}
GL . ViewportArray ( first , viewports . Length , viewportArray ) ;
GL . DepthRangeArray ( first , viewports . Length , depthRangeArray ) ;
}
2019-10-18 04:41:18 +02:00
public void TextureBarrier ( )
{
GL . MemoryBarrier ( MemoryBarrierFlags . TextureFetchBarrierBit ) ;
}
public void TextureBarrierTiled ( )
{
GL . MemoryBarrier ( MemoryBarrierFlags . TextureFetchBarrierBit ) ;
}
2019-12-29 18:41:50 +01:00
private void SetBuffer ( int index , ShaderStage stage , BufferRange buffer , bool isStorage )
{
int bindingPoint = isStorage
? _program . GetStorageBufferBindingPoint ( stage , index )
: _program . GetUniformBufferBindingPoint ( stage , index ) ;
if ( bindingPoint = = - 1 )
{
return ;
}
BufferRangeTarget target = isStorage
? BufferRangeTarget . ShaderStorageBuffer
: BufferRangeTarget . UniformBuffer ;
2020-05-23 11:46:09 +02:00
if ( buffer . Handle = = null )
2019-12-29 18:41:50 +01:00
{
GL . BindBufferRange ( target , bindingPoint , 0 , IntPtr . Zero , 0 ) ;
return ;
}
IntPtr bufferOffset = ( IntPtr ) buffer . Offset ;
2020-05-23 11:46:09 +02:00
GL . BindBufferRange ( target , bindingPoint , buffer . Handle . ToInt32 ( ) , bufferOffset , buffer . Size ) ;
2019-12-29 18:41:50 +01:00
}
2019-10-13 08:02:07 +02:00
private void SetOrigin ( ClipOrigin origin )
{
if ( _clipOrigin ! = origin )
{
_clipOrigin = origin ;
2019-12-07 05:54:28 +01:00
GL . ClipControl ( origin , _clipDepthMode ) ;
2019-10-13 08:02:07 +02:00
}
}
private void EnsureVertexArray ( )
{
if ( _vertexArray = = null )
{
_vertexArray = new VertexArray ( ) ;
_vertexArray . Bind ( ) ;
}
}
private void EnsureFramebuffer ( )
{
if ( _framebuffer = = null )
{
_framebuffer = new Framebuffer ( ) ;
2020-05-24 15:44:12 +02:00
int boundHandle = _framebuffer . Bind ( ) ;
_boundDrawFramebuffer = _boundReadFramebuffer = boundHandle ;
2019-10-13 08:02:07 +02:00
GL . Enable ( EnableCap . FramebufferSrgb ) ;
}
}
2020-05-24 15:44:12 +02:00
internal ( int drawHandle , int readHandle ) GetBoundFramebuffers ( )
{
return ( _boundDrawFramebuffer , _boundReadFramebuffer ) ;
}
2019-10-13 08:02:07 +02:00
private void UpdateDepthTest ( )
{
// Enabling depth operations is only valid when we have
// a depth buffer, otherwise it's not allowed.
if ( _hasDepthBuffer )
{
if ( _depthTest )
{
GL . Enable ( EnableCap . DepthTest ) ;
}
else
{
GL . Disable ( EnableCap . DepthTest ) ;
}
GL . DepthMask ( _depthMask ) ;
}
else
{
GL . Disable ( EnableCap . DepthTest ) ;
GL . DepthMask ( false ) ;
}
}
2019-11-27 04:41:22 +01:00
private void PrepareForDispatch ( )
{
if ( _unit0Texture ! = null )
{
_unit0Texture . Bind ( 0 ) ;
}
}
2019-10-13 08:02:07 +02:00
private void PrepareForDraw ( )
{
_vertexArray . Validate ( ) ;
if ( _unit0Texture ! = null )
{
_unit0Texture . Bind ( 0 ) ;
}
}
private void RestoreComponentMask ( int index )
{
2020-05-23 11:46:09 +02:00
GL . ColorMask (
index ,
( _componentMasks [ index ] & 1 u ) ! = 0 ,
( _componentMasks [ index ] & 2 u ) ! = 0 ,
( _componentMasks [ index ] & 4 u ) ! = 0 ,
( _componentMasks [ index ] & 8 u ) ! = 0 ) ;
2019-10-13 08:02:07 +02:00
}
2019-12-31 23:09:49 +01:00
2020-04-07 11:19:45 +02:00
public void RestoreScissor0Enable ( )
2020-03-29 05:02:58 +02:00
{
2020-04-07 11:19:45 +02:00
if ( _scissor0Enable )
2020-03-29 05:02:58 +02:00
{
2020-04-07 11:19:45 +02:00
GL . Enable ( IndexedEnableCap . ScissorTest , 0 ) ;
}
}
public void RestoreRasterizerDiscard ( )
{
if ( _rasterizerDiscard )
{
GL . Enable ( EnableCap . RasterizerDiscard ) ;
2020-03-29 05:02:58 +02:00
}
}
2020-05-04 04:24:59 +02:00
public bool TryHostConditionalRendering ( ICounterEvent value , ulong compare , bool isEqual )
{
if ( value is CounterQueueEvent )
{
// Compare an event and a constant value.
CounterQueueEvent evt = ( CounterQueueEvent ) value ;
// Easy host conditional rendering when the check matches what GL can do:
// - Event is of type samples passed.
// - Result is not a combination of multiple queries.
// - Comparing against 0.
// - Event has not already been flushed.
if ( evt . Disposed )
{
// If the event has been flushed, then just use the values on the CPU.
// The query object may already be repurposed for another draw (eg. begin + end).
return false ;
}
if ( compare = = 0 & & evt . Type = = QueryTarget . SamplesPassed & & evt . ClearCounter )
{
GL . BeginConditionalRender ( evt . Query , isEqual ? ConditionalRenderType . QueryNoWaitInverted : ConditionalRenderType . QueryNoWait ) ;
return true ;
}
}
2020-05-27 09:51:03 +02:00
// The GPU will flush the queries to CPU and evaluate the condition there instead.
GL . Flush ( ) ; // The thread will be stalled manually flushing the counter, so flush GL commands now.
return false ;
2020-05-04 04:24:59 +02:00
}
public bool TryHostConditionalRendering ( ICounterEvent value , ICounterEvent compare , bool isEqual )
{
2020-05-27 09:51:03 +02:00
GL . Flush ( ) ; // The GPU thread will be stalled manually flushing the counter, so flush GL commands now.
2020-05-04 04:24:59 +02:00
return false ; // We don't currently have a way to compare two counters for conditional rendering.
}
public void EndHostConditionalRendering ( )
{
GL . EndConditionalRender ( ) ;
}
2019-12-31 23:09:49 +01:00
public void Dispose ( )
{
_framebuffer ? . Dispose ( ) ;
_vertexArray ? . Dispose ( ) ;
}
2020-07-07 04:41:07 +02:00
public void UpdateRenderScale ( ShaderStage stage , int textureCount )
{
if ( _program ! = null )
{
switch ( stage )
{
case ShaderStage . Fragment :
if ( _program . FragmentRenderScaleUniform ! = - 1 )
{
GL . Uniform1 ( _program . FragmentRenderScaleUniform , textureCount + 1 , _fpRenderScale ) ;
}
break ;
case ShaderStage . Compute :
if ( _program . ComputeRenderScaleUniform ! = - 1 )
{
GL . Uniform1 ( _program . ComputeRenderScaleUniform , textureCount , _cpRenderScale ) ;
}
break ;
}
}
}
2019-10-13 08:02:07 +02:00
}
}