unity_native_render_plugin/NativeRenderPlugin/RenderAPI_OpenGLCoreES.cpp

315 lines
9.1 KiB
C++
Raw Normal View History

2024-11-01 16:55:46 +08:00
#include "RenderAPI.h"
#include "PlatformBase.h"
// OpenGL Core profile (desktop) or OpenGL ES (mobile) implementation of RenderAPI.
// Supports several flavors: Core, ES2, ES3
#if SUPPORT_OPENGL_UNIFIED
#include <assert.h>
#if UNITY_IOS || UNITY_TVOS
# include <OpenGLES/ES2/gl.h>
#elif UNITY_ANDROID || UNITY_WEBGL
# include <GLES2/gl2.h>
#elif UNITY_OSX
# include <OpenGL/gl3.h>
#elif UNITY_WIN
// On Windows, use gl3w to initialize and load OpenGL Core functions. In principle any other
// library (like GLEW, GLFW etc.) can be used; here we use gl3w since it's simple and
// straightforward.
# include "gl3w/gl3w.h"
#elif UNITY_LINUX
# define GL_GLEXT_PROTOTYPES
# include <GL/gl.h>
#elif UNITY_EMBEDDED_LINUX
# include <GLES2/gl2.h>
#if SUPPORT_OPENGL_CORE
# define GL_GLEXT_PROTOTYPES
# include <GL/gl.h>
#endif
#elif UNITY_QNX
# include <GLES2/gl2.h>
#else
# error Unknown platform
#endif
class RenderAPI_OpenGLCoreES : public RenderAPI
{
public:
RenderAPI_OpenGLCoreES(UnityGfxRenderer apiType);
virtual ~RenderAPI_OpenGLCoreES() { }
virtual void ProcessDeviceEvent(UnityGfxDeviceEventType type, IUnityInterfaces* interfaces);
virtual bool GetUsesReverseZ() { return false; }
virtual void DrawSimpleTriangles(const float worldMatrix[16], int triangleCount, const void* verticesFloat3Byte4);
virtual void* BeginModifyTexture(void* textureHandle, int textureWidth, int textureHeight, int* outRowPitch);
virtual void EndModifyTexture(void* textureHandle, int textureWidth, int textureHeight, int rowPitch, void* dataPtr);
virtual void* BeginModifyVertexBuffer(void* bufferHandle, size_t* outBufferSize);
virtual void EndModifyVertexBuffer(void* bufferHandle);
private:
void CreateResources();
private:
UnityGfxRenderer m_APIType;
GLuint m_VertexShader;
GLuint m_FragmentShader;
GLuint m_Program;
GLuint m_VertexArray;
GLuint m_VertexBuffer;
int m_UniformWorldMatrix;
int m_UniformProjMatrix;
};
RenderAPI* CreateRenderAPI_OpenGLCoreES(UnityGfxRenderer apiType)
{
return new RenderAPI_OpenGLCoreES(apiType);
}
enum VertexInputs
{
kVertexInputPosition = 0,
kVertexInputColor = 1
};
// Simple vertex shader source
#define VERTEX_SHADER_SRC(ver, attr, varying) \
ver \
attr " highp vec3 pos;\n" \
attr " lowp vec4 color;\n" \
"\n" \
varying " lowp vec4 ocolor;\n" \
"\n" \
"uniform highp mat4 worldMatrix;\n" \
"uniform highp mat4 projMatrix;\n" \
"\n" \
"void main()\n" \
"{\n" \
" gl_Position = (projMatrix * worldMatrix) * vec4(pos,1);\n" \
" ocolor = color;\n" \
"}\n" \
static const char* kGlesVProgTextGLES2 = VERTEX_SHADER_SRC("\n", "attribute", "varying");
static const char* kGlesVProgTextGLES3 = VERTEX_SHADER_SRC("#version 300 es\n", "in", "out");
#if SUPPORT_OPENGL_CORE
static const char* kGlesVProgTextGLCore = VERTEX_SHADER_SRC("#version 150\n", "in", "out");
#endif
#undef VERTEX_SHADER_SRC
// Simple fragment shader source
#define FRAGMENT_SHADER_SRC(ver, varying, outDecl, outVar) \
ver \
outDecl \
varying " lowp vec4 ocolor;\n" \
"\n" \
"void main()\n" \
"{\n" \
" " outVar " = ocolor;\n" \
"}\n" \
static const char* kGlesFShaderTextGLES2 = FRAGMENT_SHADER_SRC("\n", "varying", "\n", "gl_FragColor");
static const char* kGlesFShaderTextGLES3 = FRAGMENT_SHADER_SRC("#version 300 es\n", "in", "out lowp vec4 fragColor;\n", "fragColor");
#if SUPPORT_OPENGL_CORE
static const char* kGlesFShaderTextGLCore = FRAGMENT_SHADER_SRC("#version 150\n", "in", "out lowp vec4 fragColor;\n", "fragColor");
#endif
#undef FRAGMENT_SHADER_SRC
static GLuint CreateShader(GLenum type, const char* sourceText)
{
GLuint ret = glCreateShader(type);
glShaderSource(ret, 1, &sourceText, NULL);
glCompileShader(ret);
return ret;
}
void RenderAPI_OpenGLCoreES::CreateResources()
{
# if UNITY_WIN && SUPPORT_OPENGL_CORE
if (m_APIType == kUnityGfxRendererOpenGLCore)
gl3wInit();
# endif
// Make sure that there are no GL error flags set before creating resources
while (glGetError() != GL_NO_ERROR) {}
// Create shaders
if (m_APIType == kUnityGfxRendererOpenGLES30)
{
m_VertexShader = CreateShader(GL_VERTEX_SHADER, kGlesVProgTextGLES2);
m_FragmentShader = CreateShader(GL_FRAGMENT_SHADER, kGlesFShaderTextGLES2);
}
else if (m_APIType == kUnityGfxRendererOpenGLES30)
{
m_VertexShader = CreateShader(GL_VERTEX_SHADER, kGlesVProgTextGLES3);
m_FragmentShader = CreateShader(GL_FRAGMENT_SHADER, kGlesFShaderTextGLES3);
}
# if SUPPORT_OPENGL_CORE
else if (m_APIType == kUnityGfxRendererOpenGLCore)
{
m_VertexShader = CreateShader(GL_VERTEX_SHADER, kGlesVProgTextGLCore);
m_FragmentShader = CreateShader(GL_FRAGMENT_SHADER, kGlesFShaderTextGLCore);
}
# endif // if SUPPORT_OPENGL_CORE
// Link shaders into a program and find uniform locations
m_Program = glCreateProgram();
glBindAttribLocation(m_Program, kVertexInputPosition, "pos");
glBindAttribLocation(m_Program, kVertexInputColor, "color");
glAttachShader(m_Program, m_VertexShader);
glAttachShader(m_Program, m_FragmentShader);
# if SUPPORT_OPENGL_CORE
if (m_APIType == kUnityGfxRendererOpenGLCore)
glBindFragDataLocation(m_Program, 0, "fragColor");
# endif // if SUPPORT_OPENGL_CORE
glLinkProgram(m_Program);
GLint status = 0;
glGetProgramiv(m_Program, GL_LINK_STATUS, &status);
assert(status == GL_TRUE);
m_UniformWorldMatrix = glGetUniformLocation(m_Program, "worldMatrix");
m_UniformProjMatrix = glGetUniformLocation(m_Program, "projMatrix");
// Create vertex buffer
glGenBuffers(1, &m_VertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_VertexBuffer);
glBufferData(GL_ARRAY_BUFFER, 1024, NULL, GL_STREAM_DRAW);
assert(glGetError() == GL_NO_ERROR);
}
RenderAPI_OpenGLCoreES::RenderAPI_OpenGLCoreES(UnityGfxRenderer apiType)
: m_APIType(apiType)
{
}
void RenderAPI_OpenGLCoreES::ProcessDeviceEvent(UnityGfxDeviceEventType type, IUnityInterfaces* interfaces)
{
if (type == kUnityGfxDeviceEventInitialize)
{
CreateResources();
}
else if (type == kUnityGfxDeviceEventShutdown)
{
//@TODO: release resources
}
}
void RenderAPI_OpenGLCoreES::DrawSimpleTriangles(const float worldMatrix[16], int triangleCount, const void* verticesFloat3Byte4)
{
// Set basic render state
glDisable(GL_CULL_FACE);
glDisable(GL_BLEND);
glDepthFunc(GL_LEQUAL);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
// Tweak the projection matrix a bit to make it match what identity projection would do in D3D case.
float projectionMatrix[16] = {
1,0,0,0,
0,1,0,0,
0,0,2,0,
0,0,-1,1,
};
// Setup shader program to use, and the matrices
glUseProgram(m_Program);
glUniformMatrix4fv(m_UniformWorldMatrix, 1, GL_FALSE, worldMatrix);
glUniformMatrix4fv(m_UniformProjMatrix, 1, GL_FALSE, projectionMatrix);
// Core profile needs VAOs, setup one
# if SUPPORT_OPENGL_CORE
if (m_APIType == kUnityGfxRendererOpenGLCore)
{
glGenVertexArrays(1, &m_VertexArray);
glBindVertexArray(m_VertexArray);
}
# endif // if SUPPORT_OPENGL_CORE
// Bind a vertex buffer, and update data in it
const int kVertexSize = 12 + 4;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, m_VertexBuffer);
glBufferSubData(GL_ARRAY_BUFFER, 0, kVertexSize * triangleCount * 3, verticesFloat3Byte4);
// Setup vertex layout
glEnableVertexAttribArray(kVertexInputPosition);
glVertexAttribPointer(kVertexInputPosition, 3, GL_FLOAT, GL_FALSE, kVertexSize, (char*)NULL + 0);
glEnableVertexAttribArray(kVertexInputColor);
glVertexAttribPointer(kVertexInputColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, kVertexSize, (char*)NULL + 12);
// Draw
glDrawArrays(GL_TRIANGLES, 0, triangleCount * 3);
// Cleanup VAO
# if SUPPORT_OPENGL_CORE
if (m_APIType == kUnityGfxRendererOpenGLCore)
{
glDeleteVertexArrays(1, &m_VertexArray);
}
# endif
}
void* RenderAPI_OpenGLCoreES::BeginModifyTexture(void* textureHandle, int textureWidth, int textureHeight, int* outRowPitch)
{
const int rowPitch = textureWidth * 4;
// Just allocate a system memory buffer here for simplicity
unsigned char* data = new unsigned char[rowPitch * textureHeight];
*outRowPitch = rowPitch;
return data;
}
void RenderAPI_OpenGLCoreES::EndModifyTexture(void* textureHandle, int textureWidth, int textureHeight, int rowPitch, void* dataPtr)
{
GLuint gltex = (GLuint)(size_t)(textureHandle);
// Update texture data, and free the memory buffer
glBindTexture(GL_TEXTURE_2D, gltex);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, textureWidth, textureHeight, GL_RGBA, GL_UNSIGNED_BYTE, dataPtr);
delete[](unsigned char*)dataPtr;
}
void* RenderAPI_OpenGLCoreES::BeginModifyVertexBuffer(void* bufferHandle, size_t* outBufferSize)
{
# if SUPPORT_OPENGL_ES
return 0;
# else
glBindBuffer(GL_ARRAY_BUFFER, (GLuint)(size_t)bufferHandle);
GLint size = 0;
glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &size);
*outBufferSize = size;
void* mapped = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
return mapped;
# endif
}
void RenderAPI_OpenGLCoreES::EndModifyVertexBuffer(void* bufferHandle)
{
# if !SUPPORT_OPENGL_ES
glBindBuffer(GL_ARRAY_BUFFER, (GLuint)(size_t)bufferHandle);
glUnmapBuffer(GL_ARRAY_BUFFER);
# endif
}
#endif // #if SUPPORT_OPENGL_UNIFIED