microsoft / angle

ANGLE: OpenGL ES to DirectX translation
Other
615 stars 166 forks source link

AppForOpenGLES Xaml Interop in .NET for C# #125

Closed JimSEOW closed 7 years ago

JimSEOW commented 7 years ago

I am working on a AppForOpenGLES c# template based on the C++ OpenGLES template that comes with VS2015

It is based on the latest ANGLE.WINDOWSTORE nuget

The SimpleText works in W10 and W10M (950XL)

=> Could someone with experience doing ANGLE in C# to take a look why the SimpleTriangle Renderer is not working.

If someone has port the C++ template to C#, please share a link.

relevant issue1 and issue2

austinkinross commented 7 years ago

Hi, could you clarify what you mean when you say it "is not working"? Do you get any error messages?

JimSEOW commented 7 years ago

the triangle example follow this I bring the c++ code to VS2015 OpenGLES XAML c++ template and it works.

SimpleTriangle::SimpleTriangle() :
    mWindowWidth(0),
    mWindowHeight(0)
{
    // Vertex Shader source
    const std::string vs = STRING
    (
        attribute vec4 vPosition;
        void main()
        {
            gl_Position = vPosition;
        }
    );

    // Fragment Shader source
    const std::string fs = STRING
    (
        precision mediump float;
        void main()
        {
            gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
        }
    );

    // Set up the shader and its uniform/attribute locations.
    mProgram = CompileProgram1(vs, fs);
    mPositionAttribLocation = glGetAttribLocation(mProgram, "vPosition");

    GLfloat vertexPositions[] = 
    { 
        0.0f,  0.5f, 0.0f,
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f,  0.0f 
    };

    glGenBuffers(1, &mVertexPositionBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, mVertexPositionBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);
}

SimpleTriangle::~SimpleTriangle()
{
    if (mProgram != 0)
    {
        glDeleteProgram(mProgram);
        mProgram = 0;
    }

    if (mVertexPositionBuffer != 0)
    {
        glDeleteBuffers(1, &mVertexPositionBuffer);
        mVertexPositionBuffer = 0;
    }
}

void SimpleTriangle::Draw()
{
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

    // Clear the color buffer
    glClear(GL_COLOR_BUFFER_BIT);

    if (mProgram == 0)
        return;

    // Use the program object
    glUseProgram(mProgram);

    glBindBuffer(GL_ARRAY_BUFFER, mVertexPositionBuffer);
    glEnableVertexAttribArray(mPositionAttribLocation);
    glVertexAttribPointer(mPositionAttribLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);

    glDrawArrays(GL_TRIANGLES, 0, 3);
}

void SimpleTriangle::UpdateWindowSize(GLsizei width, GLsizei height)
{
    glViewport(0, 0, width, height);
    mWindowWidth = width;
    mWindowHeight = height;
}

2017-07-09_11-44-59

For the C#, I port the same thing. There is no error. Just no triangle.

JimSEOW commented 7 years ago

The SimpleTriangle.cs code `

using System;
using System.Text;
using WebGL;
using Windows.Foundation;
using System.Runtime.InteropServices;
using EGLDisplay = System.IntPtr;
using GLenum = System.UInt32;
using GLuint = System.UInt32;
using GLint = System.Int32;
using GLchar = System.Byte;
using GLsizei = System.Int32;
using GLfloat = System.Single;
namespace AppForOpenGLES
{
    /// <summary>
    /// 
    https://www.khronos.org/assets/uploads/books/openglr_es_20_programming_guide_sample.pdf
    /// </summary>
    public class SimpleTriangle : IDisposable
    {
        #region Private
        private uint mProgram;
        private float mWindowWidth;
        private float mWindowHeight;

        private uint mPositionAttribLocation;
        private uint[] mVertexPositionBuffer;
        #endregion

        public SimpleTriangle()
        {
            string vs = @"                      
              attribute vec4 vPosition;
              void main()                            
              {                                      
                 gl_Position = vPosition;            
              }";

            string fs = @"                  
              precision mediump float;                                            
              void main()                                 
              {                                           
                 gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
              }";

            // Set up the shader and its uniform/attribute locations.
            mProgram = CompileProgram(vs, fs);
            mPositionAttribLocation = (uint)GLES.glGetAttribLocation(mProgram, "vPosition");

            GLfloat[] vertexPositions = 
            {
                0.0f,  0.5f, 0.0f,
               -0.5f, -0.5f, 0.0f,
                0.5f, -0.5f, 0.0f
            };

            mVertexPositionBuffer = new uint[1];
            GLES.glGenBuffers(1, mVertexPositionBuffer); 
            GLES.glBindBuffer(GLES.GL_ARRAY_BUFFER, mVertexPositionBuffer[0]);
            GLES.glBufferData(GLES.GL_ARRAY_BUFFER, vertexPositions.Length, InPtr(vertexPositions), GLES.GL_STATIC_DRAW);
        }

        public void Dispose()
        {
            if (mProgram != 0)
            {
                GLES.glDeleteProgram(mProgram);
                mProgram = 0;
            }

            if (mVertexPositionBuffer.Length != 0)
            {
                GLES.glDeleteBuffers(1, mVertexPositionBuffer);
                mVertexPositionBuffer = null;
            }
        }

        public void Draw()
        {
            GLES.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

            // Clear the color buffer
            GLES.glClear(GLES.GL_COLOR_BUFFER_BIT);

            if (mProgram == 0)
                return;

            // Use the program object
            GLES.glUseProgram(mProgram);

            // Load the vertex data
            GLES.glBindBuffer(GLES.GL_ARRAY_BUFFER, mVertexPositionBuffer[0]);
            GLES.glEnableVertexAttribArray(mPositionAttribLocation);
            GLES.glVertexAttribPointer(mPositionAttribLocation, 3, GLES.GL_FLOAT, (byte)GLES.GL_FALSE, 0, EGLDisplay.Zero);

            GLES.glDrawArrays(GLES.GL_TRIANGLES, 0, 3);
        }

        public void UpdateWindowSize(Size size)
        {
            GLES.glViewport(0, 0, (int)size.Width, (int)size.Height);
            mWindowWidth = (int)size.Width;
            mWindowHeight = (int)size.Height;
        }

        GLuint LoadShader(GLenum type, string shaderSrc )
        {
            GLuint shader;
            GLint compiled;

            // Create the shader object
            shader = GLES.glCreateShader(type);

            if (shader == 0)
            {
                return 0;
            }

            // Load the shader source
            GLES.glShaderSource(shader, 1, new string[] { @shaderSrc }, null);

            // Compile the shader
            GLES.glCompileShader(shader);

            // Check the compile status
            GLES.glGetShaderiv(shader, GLES.GL_COMPILE_STATUS,out compiled);

            if (compiled != 0)
            {
                GLint infoLen = 0;

                GLES.glGetShaderiv(shader, GLES.GL_INFO_LOG_LENGTH, out infoLen);

                if (infoLen > 1)
                {

                    int size;
                    var infolog = new StringBuilder(infoLen);
                    GLES.glGetShaderInfoLog(shader, infoLen, out size, infolog);
                    var errorMessage = "Error compiling shader: ";
                    errorMessage += infolog.ToString();
                    infolog = null;
                    throw new Exception(errorMessage);

                }

                GLES.glDeleteShader(shader);
                return 0;
            }

            return shader;
        }

        #region Compile Shader
        private GLuint CompileShader(GLenum type, string shaderSrc)
        {
            GLuint shader = GLES.glCreateShader(type);

            GLES.glShaderSource(shader, 1, new[] { @shaderSrc }, null);
            GLES.glCompileShader(shader);

            GLint compileResult;
            GLES.glGetShaderiv(shader, GLES.GL_COMPILE_STATUS, out compileResult);

            if (compileResult == 0)
            {
                int infoLogLength;
                GLES.glGetShaderiv(shader, GLES.GL_INFO_LOG_LENGTH, out infoLogLength);
                if (infoLogLength != 0)
                {
                    int size;
                    var infolog = new StringBuilder(infoLogLength);
                    GLES.glGetShaderInfoLog(shader, infoLogLength, out size, infolog);
                    var errorMessage = "Shader compilation failed: ";
                    errorMessage += infolog.ToString();
                    throw new Exception(errorMessage);
                }
            }

            return shader;
        }

        private GLuint CompileProgram(string vsSource, string fsSource)
        {
            GLuint program = GLES.glCreateProgram();

            if (program == 0)
            {
                throw new Exception("Program creation failed.");
            }

            GLuint vs = CompileShader(GLES.GL_VERTEX_SHADER, vsSource);
            GLuint fs = CompileShader(GLES.GL_FRAGMENT_SHADER, fsSource);

            if (vs == 0 || fs == 0)
            {
                GLES.glDeleteShader(fs);
                GLES.glDeleteShader(vs);
                GLES.glDeleteProgram(program);
                return 0;
            }

            GLES.glAttachShader(program, vs);
            GLES.glDeleteShader(vs);

            GLES.glAttachShader(program, fs);
            GLES.glDeleteShader(fs);

            GLES.glLinkProgram(program);

            GLint linkStatus;
            GLES.glGetProgramiv(program, GLES.GL_LINK_STATUS, out linkStatus);

            if (linkStatus == 0)
            {
                GLint infoLogLength;
                GLES.glGetProgramiv(program, GLES.GL_INFO_LOG_LENGTH, out infoLogLength);

                int size;
                var infolog = new StringBuilder(infoLogLength);
                GLES.glGetProgramInfoLog(program, infoLogLength, out size, infolog);
                var errorMessage = "Program link failed: ";
                errorMessage += infolog.ToString();
                throw new Exception(errorMessage);
            }

            return program;
        }
        #endregion
        public IntPtr InPtr(float[] FloatArray)
        {
            var handle = default(GCHandle);
            IntPtr pointer = IntPtr.Zero;
            try
            {
                handle = GCHandle.Alloc(FloatArray, GCHandleType.Pinned);
                pointer = handle.AddrOfPinnedObject();
            }
            finally
            {
                if (handle.IsAllocated)
                    handle.Free();
            }
            return pointer;
        }
    }

}

`

JimSEOW commented 7 years ago

There are 2 types of C++ OpenGL ES templates for VS2015 [1] XAML App for OpenGL ES (Universal Windows) [2] Visual Studio template for cross-platform OpenGL development The approach used here follow [1]: How to combine OpenGL graphics with XAML controls in a single window

JimSEOW commented 7 years ago

@austinkinross The concept of just using PInvoke to allow C# binding to Angle is straight forwards. However, the lack of users trying this reveal many challenges

JimSEOW commented 7 years ago

For people interested at OpenGLES for UWP XAML Interop in C#, please also take a look at SkiaSharp.View.UWP, particularly OpenGLESContext in SkiaSharp.View.UWP

JimSEOW commented 7 years ago

bringing OpenGL ES (Angle) to XAML Standard for 3D

JimSEOW commented 7 years ago

Solved!!!