diff --git a/src/Graphics/OpenGLDevice.cs b/src/Graphics/OpenGLDevice.cs index 907e601..6313ec8 100644 --- a/src/Graphics/OpenGLDevice.cs +++ b/src/Graphics/OpenGLDevice.cs @@ -473,6 +473,7 @@ namespace Microsoft.Xna.Framework.Graphics private bool supportsMultisampling; private bool supportsFauxBackbuffer; + private bool supportsBaseVertex; #endregion @@ -1126,7 +1127,7 @@ namespace Microsoft.Xna.Framework.Graphics BindIndexBuffer(indices.buffer); // Draw! - glDrawRangeElements( + glDrawRangeElementsBaseVertex( XNAToGL.Primitive[(int) primitiveType], minVertexIndex, minVertexIndex + numVertices - 1, @@ -1134,7 +1135,8 @@ namespace Microsoft.Xna.Framework.Graphics shortIndices ? GLenum.GL_UNSIGNED_SHORT : GLenum.GL_UNSIGNED_INT, - (IntPtr) (startIndex * (shortIndices ? 2 : 4)) + (IntPtr) (startIndex * (shortIndices ? 2 : 4)), + baseVertex ); } @@ -1157,14 +1159,15 @@ namespace Microsoft.Xna.Framework.Graphics bool shortIndices = indices.IndexElementSize == IndexElementSize.SixteenBits; // Draw! - glDrawElementsInstanced( + glDrawElementsInstancedBaseVertex( XNAToGL.Primitive[(int) primitiveType], XNAToGL.PrimitiveVerts(primitiveType, primitiveCount), shortIndices ? GLenum.GL_UNSIGNED_SHORT : GLenum.GL_UNSIGNED_INT, (IntPtr) (startIndex * (shortIndices ? 2 : 4)), - instanceCount + instanceCount, + baseVertex ); } @@ -1906,6 +1909,11 @@ namespace Microsoft.Xna.Framework.Graphics bool bindingsUpdated, int baseVertex ) { + if (supportsBaseVertex) + { + baseVertex = 0; + } + if ( bindingsUpdated || baseVertex != ldBaseVertex || currentEffect != ldEffect || diff --git a/src/Graphics/OpenGLDevice_GL.cs b/src/Graphics/OpenGLDevice_GL.cs index e8ce0cb..782d571 100644 --- a/src/Graphics/OpenGLDevice_GL.cs +++ b/src/Graphics/OpenGLDevice_GL.cs @@ -720,6 +720,27 @@ namespace Microsoft.Xna.Framework.Graphics ); private DrawRangeElements glDrawRangeElements; + private delegate void DrawElementsInstancedBaseVertex( + GLenum mode, + int count, + GLenum type, + IntPtr indices, + int instanceCount, + int baseVertex + ); + private DrawElementsInstancedBaseVertex glDrawElementsInstancedBaseVertex; + + private delegate void DrawRangeElementsBaseVertex( + GLenum mode, + int start, + int end, + int count, + GLenum type, + IntPtr indices, + int baseVertex + ); + private DrawRangeElementsBaseVertex glDrawRangeElementsBaseVertex; + private delegate void DrawElements( GLenum mode, int count, @@ -1045,27 +1066,41 @@ namespace Microsoft.Xna.Framework.Graphics throw new NoSuitableGraphicsDeviceException(baseErrorString); } - /* DrawRangeElements is better, but some ES2 targets don't have it. */ - IntPtr ep = SDL.SDL_GL_GetProcAddress("glDrawRangeElements"); - if (ep != IntPtr.Zero) + /* ARB_draw_elements_base_vertex is ideal! */ + IntPtr ep = SDL.SDL_GL_GetProcAddress("glDrawRangeElementsBaseVertex"); + supportsBaseVertex = ep != IntPtr.Zero; + if (supportsBaseVertex) { - glDrawRangeElements = (DrawRangeElements) Marshal.GetDelegateForFunctionPointer( + glDrawRangeElementsBaseVertex = (DrawRangeElementsBaseVertex) Marshal.GetDelegateForFunctionPointer( ep, - typeof(DrawRangeElements) + typeof(DrawRangeElementsBaseVertex) ); } else { - ep = SDL.SDL_GL_GetProcAddress("glDrawElements"); - if (ep == IntPtr.Zero) + /* DrawRangeElements is better, but some ES2 targets don't have it. */ + ep = SDL.SDL_GL_GetProcAddress("glDrawRangeElements"); + if (ep != IntPtr.Zero) { - throw new NoSuitableGraphicsDeviceException(baseErrorString); + glDrawRangeElements = (DrawRangeElements) Marshal.GetDelegateForFunctionPointer( + ep, + typeof(DrawRangeElements) + ); + glDrawRangeElementsBaseVertex = DrawRangeElementsNoBase; + } + else + { + ep = SDL.SDL_GL_GetProcAddress("glDrawElements"); + if (ep == IntPtr.Zero) + { + throw new NoSuitableGraphicsDeviceException(baseErrorString); + } + glDrawElements = (DrawElements) Marshal.GetDelegateForFunctionPointer( + ep, + typeof(DrawElements) + ); + glDrawRangeElementsBaseVertex = DrawRangeElementsNoBaseUnchecked; } - glDrawElements = (DrawElements) Marshal.GetDelegateForFunctionPointer( - ep, - typeof(DrawElements) - ); - glDrawRangeElements = DrawRangeElementsUnchecked; } /* These functions are NOT supported in ES. @@ -1294,10 +1329,22 @@ namespace Microsoft.Xna.Framework.Graphics SDL.SDL_GL_GetProcAddress("glVertexAttribDivisor"), typeof(VertexAttribDivisor) ); - glDrawElementsInstanced = (DrawElementsInstanced) Marshal.GetDelegateForFunctionPointer( - SDL.SDL_GL_GetProcAddress("glDrawElementsInstanced"), - typeof(DrawElementsInstanced) - ); + /* The likelihood of someone having BaseVertex but not Instanced is 0...? */ + if (supportsBaseVertex) + { + glDrawElementsInstancedBaseVertex = (DrawElementsInstancedBaseVertex) Marshal.GetDelegateForFunctionPointer( + SDL.SDL_GL_GetProcAddress("glDrawElementsInstancedBaseVertex"), + typeof(DrawElementsInstancedBaseVertex) + ); + } + else + { + glDrawElementsInstanced = (DrawElementsInstanced) Marshal.GetDelegateForFunctionPointer( + SDL.SDL_GL_GetProcAddress("glDrawElementsInstanced"), + typeof(DrawElementsInstanced) + ); + glDrawElementsInstancedBaseVertex = DrawElementsInstancedNoBase; + } } catch { @@ -1433,13 +1480,33 @@ namespace Microsoft.Xna.Framework.Graphics return result; } - private void DrawRangeElementsUnchecked( + private void DrawRangeElementsNoBase( GLenum mode, int start, int end, int count, GLenum type, - IntPtr indices + IntPtr indices, + int baseVertex + ) { + glDrawRangeElements( + mode, + start, + end, + count, + type, + indices + ); + } + + private void DrawRangeElementsNoBaseUnchecked( + GLenum mode, + int start, + int end, + int count, + GLenum type, + IntPtr indices, + int baseVertex ) { glDrawElements( mode, @@ -1449,6 +1516,23 @@ namespace Microsoft.Xna.Framework.Graphics ); } + private void DrawElementsInstancedNoBase( + GLenum mode, + int count, + GLenum type, + IntPtr indices, + int instanceCount, + int baseVertex + ) { + glDrawElementsInstanced( + mode, + count, + type, + indices, + instanceCount + ); + } + private void DepthRangeFloat(double near, double far) { glDepthRangef((float) near, (float) far); diff --git a/src/Graphics/SpriteBatch.cs b/src/Graphics/SpriteBatch.cs index a2b0608..9766527 100644 --- a/src/Graphics/SpriteBatch.cs +++ b/src/Graphics/SpriteBatch.cs @@ -849,20 +849,18 @@ namespace Microsoft.Xna.Framework.Graphics spriteData[numSprites].depth = depth; spriteData[numSprites].effects = effects; + numSprites += 1; if (sortMode == SpriteSortMode.Immediate) { - RenderBatch(0, 1); - } - else - { - numSprites += 1; + PushVertices(); + DrawPrimitives(texture, 0, 1); + numSprites = 0; } } - private void RenderBatch(int offset, int batchSize) + private void PushVertices() { - GraphicsDevice.Textures[0] = spriteData[offset].texture; - for (int i = 0; i < batchSize; i += 1) + for (int i = 0; i < numSprites; i += 1) { /* FIXME: OPTIMIZATION POINT: This method * allocates like fuck right now! In general, @@ -872,7 +870,7 @@ namespace Microsoft.Xna.Framework.Graphics */ // Current sprite being calculated - SpriteInfo info = spriteData[i + offset]; + SpriteInfo info = spriteData[i]; // Calculate initial sprite information Vector2 source = new Vector2( @@ -901,8 +899,8 @@ namespace Microsoft.Xna.Framework.Graphics // Calculations performed with inverse texture size Vector2 inverseTexSize = new Vector2( - (1.0f / (float) spriteData[offset].texture.Width), - (1.0f / (float) spriteData[offset].texture.Height) + (1.0f / (float) info.texture.Width), + (1.0f / (float) info.texture.Height) ); if ((info.effects & SpriteInfo.SourceInTexels) == SpriteInfo.SourceInTexels) { @@ -917,8 +915,8 @@ namespace Microsoft.Xna.Framework.Graphics // Calculations done with texture size if ((info.effects & SpriteInfo.DestSizeInPixels) != SpriteInfo.DestSizeInPixels) { - destinationSize.X *= spriteData[offset].texture.Width; - destinationSize.Y *= spriteData[offset].texture.Height; + destinationSize.X *= info.texture.Width; + destinationSize.Y *= info.texture.Height; } // Calculations performed with rotation @@ -974,34 +972,7 @@ namespace Microsoft.Xna.Framework.Graphics ); } } - vertexBuffer.SetData(vertexInfo, 0, batchSize * 4, SetDataOptions.None); - - if (customEffect != null) - { - foreach (EffectPass pass in customEffect.CurrentTechnique.Passes) - { - pass.Apply(); - GraphicsDevice.DrawIndexedPrimitives( - PrimitiveType.TriangleList, - 0, - 0, - batchSize * 4, - 0, - batchSize * 2 - ); - } - } - else - { - GraphicsDevice.DrawIndexedPrimitives( - PrimitiveType.TriangleList, - 0, - 0, - batchSize * 4, - 0, - batchSize * 2 - ); - } + vertexBuffer.SetData(vertexInfo, 0, numSprites * 4, SetDataOptions.None); } private void FlushBatch() @@ -1046,19 +1017,19 @@ namespace Microsoft.Xna.Framework.Graphics ); } + PushVertices(); + + curTexture = spriteData[0].texture; for (int i = 0; i < numSprites; i += 1) { if (spriteData[i].texture != curTexture) { - if (i > offset) - { - RenderBatch(offset, i - offset); - } + DrawPrimitives(curTexture, offset, i - offset); curTexture = spriteData[i].texture; offset = i; } } - RenderBatch(offset, numSprites - offset); + DrawPrimitives(curTexture, offset, numSprites - offset); numSprites = 0; } @@ -1105,6 +1076,37 @@ namespace Microsoft.Xna.Framework.Graphics spriteEffectPass.Apply(); } + private void DrawPrimitives(Texture texture, int baseSprite, int batchSize) + { + GraphicsDevice.Textures[0] = texture; + if (customEffect != null) + { + foreach (EffectPass pass in customEffect.CurrentTechnique.Passes) + { + pass.Apply(); + GraphicsDevice.DrawIndexedPrimitives( + PrimitiveType.TriangleList, + baseSprite * 4, + 0, + batchSize * 4, + 0, + batchSize * 2 + ); + } + } + else + { + GraphicsDevice.DrawIndexedPrimitives( + PrimitiveType.TriangleList, + baseSprite * 4, + 0, + batchSize * 4, + 0, + batchSize * 2 + ); + } + } + [System.Diagnostics.Conditional("DEBUG")] private void CheckBegin(string method) {