315 lines
9.1 KiB
C++
315 lines
9.1 KiB
C++
|
|
#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
|