Files
FlaxEngine/Source/Engine/GraphicsDevice/OpenGL/GPUShaderOGL.cpp
2020-12-07 23:40:54 +01:00

259 lines
6.5 KiB
C++

// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#if GRAPHICS_API_OPENGL
#include "GPUShaderOGL.h"
#include "Engine/Core/Memory/Allocator.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "GPUConstantBufferOGL.h"
#include "GPUShaderProgramOGL.h"
#include "Engine/Graphics/GPULimitsOGL.h"
#include <LZ4/lz4.h>
GPUShaderOGL::GPUShaderOGL(GPUDeviceOGL* device, const String& name)
: GPUResourceOGL<Shader>(device, name)
{
}
GPUShaderOGL::~GPUShaderOGL()
{
}
void GPUShaderOGL::ReleaseGPU()
{
// Base
GPUResourceOGL::ReleaseGPU();
}
static bool SupportsSeparateShaderObjects()
{
return ((GPULimitsOGL*)GPUDevice::Instance->Limits)->SupportsSeparateShaderObjects;
}
// Verify that an OpenGL program has linked successfully.
static bool VerifyLinkedProgram(GLuint program)
{
#if BUILD_DEBUG || GPU_OGL_DEBUG_SHADERS
GLint linkStatus = 0;
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
if (linkStatus != GL_TRUE)
{
GLint logLength;
char defaultLog[] = "No log";
char* compileLog = defaultLog;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 1)
{
compileLog = (char*)Allocator::Allocate(logLength);
glGetProgramInfoLog(program, logLength, NULL, compileLog);
}
LOG(Error, "Failed to link program. Compile log: \n{0}", compileLog);
if (logLength > 1)
{
Allocator::Free(compileLog);
}
return false;
}
#endif
return true;
}
// Verify that an OpenGL shader has compiled successfully.
static bool VerifyCompiledShader(GLuint shader, const char* glslCode)
{
#if BUILD_DEBUG || GPU_OGL_DEBUG_SHADERS
if (SupportsSeparateShaderObjects() && glIsProgram(shader))
{
bool const compiledOK = VerifyLinkedProgram(shader);
#if GPU_OGL_DEBUG_SHADERS
if (!compiledOK && glslCode)
{
LOG(Warning, "Shader: \n{0}", glslCode);
}
#endif
return compiledOK;
}
else
{
GLint compileStatus;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus);
if (compileStatus != GL_TRUE)
{
GLint logLength;
char defaultLog[] = "No log";
char* compileLog = defaultLog;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
#if PLATFORM_ANDROID
if (LogLength == 0)
{
// Make it big anyway
// There was a bug in android 2.2 where glGetShaderiv would return 0 even though there was a error message
// https://code.google.com/p/android/issues/detail?id=9953
LogLength = 4096;
}
#endif
if (logLength > 1)
{
compileLog = (char*)Allocator::Allocate(logLength);
glGetShaderInfoLog(shader, logLength, NULL, compileLog);
}
#if GPU_OGL_DEBUG_SHADERS
if (glslCode)
{
LOG(Warning, "Shader: \n{0}", glslCode);
}
#endif
LOG(Fatal, "Failed to compile shader. Compile log: \n{0}", compileLog);
if (logLength > 1)
{
Allocator::Free(compileLog);
}
return false;
}
}
#endif
return true;
}
static bool VerifyProgramPipeline(GLuint Program)
{
bool result = true;
// Don't try and validate SSOs here - the draw state matters to SSOs and it definitely can't be guaranteed to be valid at this stage
if (SupportsSeparateShaderObjects())
{
#if GPU_OGL_DEBUG_SHADERS
result = glIsProgramPipeline(Program) == GL_TRUE;
#endif
}
else
{
result = VerifyLinkedProgram(Program);
}
return result;
}
bool GPUShaderOGL::Create(MemoryReadStream& stream)
{
ASSERT(_device);
ReleaseGPU();
// Version
int32 version;
stream.ReadInt32(&version);
if (version != GPU_SHADER_CACHE_VERSION)
{
LOG(Warning, "Unsupported OpenGL shader version {0}.", version);
return true;
}
Array<byte> cache;
Array<byte> cache2;
// Shaders count
int32 shadersCount;
stream.ReadInt32(&shadersCount);
for (int32 i = 0; i < shadersCount; i++)
{
uint32 cacheSize;
ShaderStage type = static_cast<ShaderStage>(stream.ReadByte());
int32 permutationsCount = stream.ReadByte();
ASSERT(Math::IsInRange(permutationsCount, 1, SHADER_PERMUTATIONS_MAX_COUNT));
// Load shader name
StringAnsi name;
stream.ReadStringAnsi(&name, 11);
for (int32 permutationIndex = 0; permutationIndex < permutationsCount; permutationIndex++)
{
// Load cache
int32 cacheType;
stream.ReadInt32(&cacheType);
uint32 cacheOriginalSize;
stream.ReadUint32(&cacheOriginalSize);
stream.ReadUint32(&cacheSize);
ASSERT(Math::IsInRange<uint32>(cacheSize, 1, 1024 * 1024));
cache.EnsureCapacity(cacheSize);
stream.ReadBytes(cache.Get(), cacheSize);
// Check if unpack the data
byte* shaderBytes = cache.Get();
if (cacheType == SHADER_DATA_FORMAT_RAW)
{
ASSERT(cacheOriginalSize == cacheSize);
}
else if (cacheType == SHADER_DATA_FORMAT_LZ4)
{
// Decompress LZ4 data
cache2.EnsureCapacity(cacheOriginalSize);
int32 res = LZ4_decompress_safe((const char*)cache.Get(), (char*)cache2.Get(), cacheSize, cacheOriginalSize);
if (res <= 0)
{
LOG(Warning, "Failed to decompress OpenGL shader data. Result: {0}.", res);
return true;
}
cacheSize = cacheOriginalSize;
shaderBytes = cache2.Get();
}
else
{
LOG(Warning, "Unknown OpenGL shader data format.");
return true;
}
// Create Flax shader object
GPUShaderProgram* shader;
switch (type)
{
case ShaderStage::Vertex: shader = New<GPUShaderProgramVSOGL>(_device, shaderBytes, cacheSize, stream, name); break;
case ShaderStage::Hull: shader = New<GPUShaderProgramHSOGL>(_device, shaderBytes, cacheSize, stream, name); break;
case ShaderStage::Domain: shader = New<GPUShaderProgramDSOGL>(_device, shaderBytes, cacheSize, stream, name); break;
case ShaderStage::Geometry: shader = New<GPUShaderProgramGSOGL>(shaderBytes, cacheSize, stream, name); break;
case ShaderStage::Pixel: shader = New<GPUShaderProgramPSOGL>(shaderBytes, cacheSize, stream, name); break;
case ShaderStage::Compute: shader = New<GPUShaderProgramCSOGL>(shaderBytes, cacheSize, stream, name); break;
default: return true;
}
// Add to collection
_shaders.Add(shader, permutationIndex);
}
}
// Constant Buffers
byte constantBuffersCount = stream.ReadByte();
byte maximumConstantBufferSlot = stream.ReadByte();
if (constantBuffersCount > 0)
{
ASSERT(maximumConstantBufferSlot < MAX_CONSTANT_BUFFER_SLOTS);
for (int32 i = 0; i < constantBuffersCount; i++)
{
// Load info
byte slotIndex = stream.ReadByte();
uint32 size;
stream.ReadUint32(&size);
// Create CB
#if GPU_ENABLE_RESOURCE_NAMING
String name = ToString() + TEXT(".CB") + i;
#else
String name;
#endif
auto cb = New<GPUConstantBufferOGL>(_device, name, size);
ASSERT(_constantBuffers[slotIndex] == nullptr);
_constantBuffers[slotIndex] = cb;
}
}
return false;
}
#endif