// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Serialization/ReadStream.h"
#include "Engine/Graphics/Shaders/GPUShaderProgram.h"
#include "../RenderToolsOGL.h"
#include "../GPUDeviceOGL.h"
#if GRAPHICS_API_OPENGL
///
/// Shaders base class for OpenGL
///
template
class GPUShaderProgramOGL : public BaseType
{
protected:
GLuint _handle;
GLenum _shaderType;
Array _shader;
#if GPU_OGL_KEEP_SHADER_SRC
const char* ShaderSource = nullptr;
#endif
public:
///
/// Init
///
/// Type of the OpenGL shader
/// Bytes with an compiled shader
/// Size in bytes of the cached shader
/// Stream with data to read
/// Shader function name
GPUShaderProgramOGL(GLenum shaderType, byte* shaderBytes, uint32 shaderBytesSize, ReadStream& stream, const StringAnsi& name)
: _handle(0)
, _shaderType(shaderType)
{
_name = name;
// Cache shader source (it cannot be compiled on content loading thread) - because OpenGL sucks
// TODO: use shared context and compile shaders on a content threads instead of main thread
_shader.Set(shaderBytes, shaderBytesSize);
// Load meta
stream.ReadUint32(&_instructionsCount);
stream.ReadUint32(&_usedCBsMask);
stream.ReadUint32(&_usedSRsMask);
stream.ReadUint32(&_usedUAsMask);
}
///
/// Destructor
///
~GPUShaderProgramOGL()
{
#if GPU_OGL_KEEP_SHADER_SRC
Allocator::Free(ShaderSource);
#endif
if (_handle != 0)
{
glDeleteProgram(_handle);
VALIDATE_OPENGL_RESULT();
}
}
private:
static GLuint glCreateGPUShaderProgram(GLenum type, const char* source)
{
const GLuint shader = glCreateShader(type);
if (shader)
{
glShaderSource(shader, 1, &source, NULL);
glCompileShader(shader);
const GLuint program = glCreateProgram();
if (program)
{
GLint compiled = GL_FALSE;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
glProgramParameteri(program, GL_PROGRAM_SEPARABLE, GL_TRUE);
if (compiled)
{
glAttachShader(program, shader);
glLinkProgram(program);
glDetachShader(program, shader);
}
}
glDeleteShader(shader);
return program;
}
return 0;
}
public:
// Gets the OpenGL shader object handle. Performs compilation if shader has not been created yet.
GLuint GetHandle()
{
if (_handle == 0)
{
// Compile the shader
_handle = glCreateGPUShaderProgram(_shaderType, (const char*)_shader.Get());
VALIDATE_OPENGL_RESULT();
// Check the status and the messages
{
GLint linkCompileSuccess = 0;
glGetProgramiv(_handle, GL_LINK_STATUS, &linkCompileSuccess);
VALIDATE_OPENGL_RESULT();
if (!linkCompileSuccess && _handle > 0)
{
GLint infologLength = 0;
glGetProgramiv(_handle, GL_INFO_LOG_LENGTH, &infologLength);
VALIDATE_OPENGL_RESULT();
if (infologLength > 0)
{
GLint charsWritten = 0;
Array infoLog;
infoLog.Resize(infologLength);
glGetProgramInfoLog(_handle, infologLength, &charsWritten, infoLog.Get());
VALIDATE_OPENGL_RESULT();
LOG(Warning, "Compile and linker info log:");
LOG_STR(Warning, String(infoLog.Get()));
}
}
}
#if GPU_OGL_KEEP_SHADER_SRC
ShaderSource = Allocator::Allocate(_shader.Count());
Platform::MemoryCopy((void*)ShaderSource, (void*)_shader.Get(), _shader.Count() * sizeof(char));
#endif
// Cleanup source buffer
_shader.Resize(0);
}
return _handle;
}
public:
// [BaseType]
uint32 GetBufferSize() const override
{
return 0;
}
void* GetBufferHandle() const override
{
return (void*)_handle;
}
};
///
/// Vertex Shader for OpenGL
///
class GPUShaderProgramVSOGL : public GPUShaderProgramOGL
{
public:
struct LayoutElement
{
int32 BufferSlot;
PixelFormat Format;
int32 Size;
int32 InstanceDataStepRate;
int32 InputIndex;
int32 RelativeOffset;
int32 TypeCount;
GLboolean Normalized;
GLenum GlType;
bool IsInteger;
};
private:
GPUDeviceOGL* _device;
public:
///
/// Init
///
/// The graphics device.
/// Bytes with an compiled shader
/// Size in bytes of the cached shader
/// Stream with data to read
/// Shader function name
GPUShaderProgramVSOGL(GPUDeviceOGL* device, byte* shaderBytes, uint32 shaderBytesSize, ReadStream& stream, const StringAnsi& name);
///
/// Destructor
///
~GPUShaderProgramVSOGL()
{
_device->VAOCache.OnObjectRelease(this);
}
public:
Array> Layout;
public:
// [GPUShaderProgramOGL]
void* GetInputLayout() const override
{
return (void*)nullptr;
}
byte GetInputLayoutSize() const override
{
return Layout.Count();
}
};
///
/// Geometry Shader for OpenGL
///
class GPUShaderProgramGSOGL : public GPUShaderProgramOGL
{
public:
///
/// Init
///
/// Bytes with an compiled shader
/// Size in bytes of the cached shader
/// Stream with data to read
/// Shader function name
GPUShaderProgramGSOGL(byte* shaderBytes, uint32 shaderBytesSize, ReadStream& stream, const StringAnsi& name)
: GPUShaderProgramOGL(GL_GEOMETRY_SHADER, shaderBytes, shaderBytesSize, stream, name)
{
}
};
///
/// Pixel Shader for OpenGL
///
class GPUShaderProgramPSOGL : public GPUShaderProgramOGL
{
public:
///
/// Init
///
/// Bytes with an compiled shader
/// Size in bytes of the cached shader
/// Stream with data to read
/// Shader function name
GPUShaderProgramPSOGL(byte* shaderBytes, uint32 shaderBytesSize, ReadStream& stream, const StringAnsi& name)
: GPUShaderProgramOGL(GL_FRAGMENT_SHADER, shaderBytes, shaderBytesSize, stream, name)
{
}
};
///
/// Compute Shader for OpenGL
///
class GPUShaderProgramCSOGL : public GPUShaderProgramOGL
{
public:
///
/// Init
///
/// Bytes with an compiled shader
/// Size in bytes of the cached shader
/// Stream with data to read
/// Shader function name
GPUShaderProgramCSOGL(byte* shaderBytes, uint32 shaderBytesSize, ReadStream& stream, const StringAnsi& name)
: GPUShaderProgramOGL(GL_COMPUTE_SHADER, shaderBytes, shaderBytesSize, stream, name)
{
}
};
#endif