// 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