1#include "EffectsGLES30.h"
2#include "ContextGLES30.h"
3#include "Foundation/StringView.h"
8#include "../Base/Utilities.h"
9#include "../Base/DefaultIOHandler.h"
17 const char* getGLTypeName(GLenum type)
20 case GL_FLOAT:
return "float";
21 case GL_FLOAT_VEC2:
return "vec2";
22 case GL_FLOAT_VEC3:
return "vec3";
23 case GL_FLOAT_VEC4:
return "vec4";
24 case GL_INT:
return "int";
25 case GL_INT_VEC2:
return "ivec2";
26 case GL_INT_VEC3:
return "ivec3";
27 case GL_INT_VEC4:
return "ivec4";
28 case GL_UNSIGNED_INT:
return "uint";
29 case GL_UNSIGNED_INT_VEC2:
return "uvec2";
30 case GL_UNSIGNED_INT_VEC3:
return "uvec3";
31 case GL_UNSIGNED_INT_VEC4:
return "uvec4";
32 case GL_BOOL:
return "bool";
33 case GL_BOOL_VEC2:
return "bvec2";
34 case GL_BOOL_VEC3:
return "bvec3";
35 case GL_BOOL_VEC4:
return "bvec4";
36 case GL_FLOAT_MAT2:
return "mat2";
37 case GL_FLOAT_MAT3:
return "mat3";
38 case GL_FLOAT_MAT4:
return "mat4";
39 case GL_FLOAT_MAT2x3:
return "mat2x3";
40 case GL_FLOAT_MAT2x4:
return "mat2x4";
41 case GL_FLOAT_MAT3x2:
return "mat3x2";
42 case GL_FLOAT_MAT3x4:
return "mat3x4";
43 case GL_FLOAT_MAT4x2:
return "mat4x2";
44 case GL_FLOAT_MAT4x3:
return "mat4x3";
45 case GL_SAMPLER_2D:
return "sampler2D";
46 case GL_SAMPLER_3D:
return "sampler3D";
47 case GL_SAMPLER_CUBE:
return "samplerCube";
48 case GL_SAMPLER_2D_SHADOW:
return "sampler2DShadow";
49 case GL_SAMPLER_2D_ARRAY:
return "sampler2DArray";
50 case GL_SAMPLER_2D_ARRAY_SHADOW:
return "sampler2DArrayShadow";
51 case GL_SAMPLER_CUBE_SHADOW:
return "samplerCubeShadow";
52 case GL_INT_SAMPLER_2D:
return "isampler2D";
53 case GL_INT_SAMPLER_3D:
return "isampler3D";
54 case GL_INT_SAMPLER_CUBE:
return "isamplerCube";
55 case GL_INT_SAMPLER_2D_ARRAY:
return "isampler2DArray";
56 case GL_UNSIGNED_INT_SAMPLER_2D:
return "usampler2D";
57 case GL_UNSIGNED_INT_SAMPLER_3D:
return "usampler3D";
58 case GL_UNSIGNED_INT_SAMPLER_CUBE:
return "usamplerCube";
59 case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
return "usampler2DArray";
60 default:
return "???";
64 void printShaderSource(GLint shaderId)
66 GLint sourceLength = 0;
67 glGetShaderiv(shaderId, GL_SHADER_SOURCE_LENGTH, &sourceLength);
69 std::vector<char> source(sourceLength);
70 glGetShaderSource(shaderId, sourceLength, &sourceLength, source.data());
72 char* start = source.data();
77 while ((*stop !=
'\0') && (*stop !=
'\n') && (*stop !=
'\r')) { stop++; }
79 LOG_DEBUG(logger,
"%3d: %s", line++, std::string(start, stop - start).c_str());
81 while ((*stop !=
'\0') && (*stop !=
'\n')) { stop++; }
82 while ((*stop !=
'\0') && (*stop ==
'\r')) { stop++; }
85 }
while (*stop !=
'\0');
91 GLint bufferLength = 0;
92 glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &bufferLength);
95 std::vector<char> buffer(bufferLength);
97 glGetShaderInfoLog(shaderId, bufferLength, NULL, buffer.data());
99 if ((effectFlags & EffectFlags::EEffectFlags::LogShaderSource) == EffectFlags::EEffectFlags::LogShaderSource) {
100 printShaderSource(shaderId);
102 LOG_ERROR(logger,
"Could not compile shader %d:\n%s\n", shaderType, buffer.data());
104 else if ((effectFlags & (EffectFlags::EEffectFlags::LogShaderInfo | EffectFlags::EEffectFlags::LogShaderSource)) == (EffectFlags::EEffectFlags::LogShaderInfo | EffectFlags::EEffectFlags::LogShaderSource)) {
105 printShaderSource(shaderId);
109 void printProgramError(GLint programId)
111 GLint bufferLength = 0;
112 glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &bufferLength);
115 std::vector<char> buffer(bufferLength);
117 glGetProgramInfoLog(programId, bufferLength, NULL, buffer.data());
119 LOG_ERROR(logger,
"Could not link program:\n%s\n", buffer.data());
126 if (!effectHandle)
return;
128 if (context->getEffect() == effectHandle) {
129 LOG_DEBUG_ONCE(logger,
"Releasing currently active effect");
134 glDeleteProgram(effect.programId);
135 effect.programId = 0;
136 effects.removeResource(effectHandle);
141 std::vector<EffectHandle> handles;
142 for (
auto& resource : effects) {
143 handles.push_back(effects.getHandle(resource));
145 for (
auto& handle : handles) {
146 releaseEffect(handle);
150GLint Cogs::EffectsGLES30::loadShader(GLenum shaderType,
const char * pSource,
PreprocessorDefinitions definitions)
154#if defined(ANDROID) || defined(__APPLE__)
155 LOG_INFO(logger,
"Loading shader from source:\n(Start source)\n%s", pSource);
156 LOG_INFO(logger,
"(End source)");
159 GLuint shaderId = glCreateShader(shaderType);
161 LOG_ERROR(logger,
"loadShader: glCreateShader failed.");
165 std::vector<std::string> source;
166 source.reserve(definitions.size() + 2);
172 while ((pSource[o] !=
'\0') && (pSource[o] !=
'#')) { o++; }
175 if ((pSource[o] ==
'#') && (strncmp(
"version", pSource + o + 1, 7) == 0)) {
177 while (pSource[o] !=
'\0' && pSource[o] !=
'\n') { o++; }
178 while (pSource[o] !=
'\0' && (pSource[o] ==
'\n' || pSource[o] ==
'\r')) { o++; }
179 source.emplace_back(pSource, pSource + o);
180 source.emplace_back(pSource + o);
183 if (source.empty()) {
184 source.push_back(pSource);
187 if (!definitions.empty()) {
188 std::string tail = std::move(source.back());
191 source.emplace_back(
"#define " + def.first +
" " + def.second +
"\n");
193 source.emplace_back(std::move(tail));
196 std::vector<const char*> strings(source.size());
197 for (
size_t i = 0, n = source.size(); i < n; i++) {
198 strings[i] = source[i].c_str();
201 glShaderSource(shaderId,
static_cast<GLsizei
>(strings.size()), strings.data(), NULL);
202 glCompileShader(shaderId);
212 std::vector<char> buffer;
214 GLuint programId = glCreateProgram();
215 if (programId == 0) {
216 LOG_ERROR(logger,
"loadEffect: glCreateProgram failed.");
219 program.programId = programId;
221 GLint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource, definitions);
222 GLint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource, definitions);
223 glAttachShader(programId, vertexShader);
224 glAttachShader(programId, pixelShader);
225 glLinkProgram(programId);
227 GLint linkStatus = GL_FALSE;
228 glGetProgramiv(programId, GL_LINK_STATUS, &linkStatus);
229 if (linkStatus != GL_TRUE) {
230 printShaderError(GL_VERTEX_SHADER, vertexShader, effectFlags);
231 printShaderError(GL_FRAGMENT_SHADER, pixelShader, effectFlags);
232 printProgramError(programId);
234 glDeleteShader(vertexShader);
235 glDeleteShader(pixelShader);
236 glDeleteProgram(programId);
238 LOG_ERROR(logger,
"loadEffect: Failed to link shader program.");
244 printShaderSource(vertexShader);
245 printShaderSource(pixelShader);
248 printShaderError(GL_VERTEX_SHADER, vertexShader, effectFlags);
249 printShaderError(GL_FRAGMENT_SHADER, pixelShader, effectFlags);
251 glDeleteShader(vertexShader);
252 glDeleteShader(pixelShader);
255 GLint activeAttributes = 0;
256 glGetProgramiv(programId, GL_ACTIVE_ATTRIBUTES, &activeAttributes);
258 GLint attributeMaxLength = 0;
259 glGetProgramiv(programId, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &attributeMaxLength);
262 GLint activeUniforms = 0;
263 glGetProgramiv(programId, GL_ACTIVE_UNIFORMS, &activeUniforms);
265 GLint activeUniformBlocks = 0;
266 glGetProgramiv(programId, GL_ACTIVE_UNIFORM_BLOCKS, &activeUniformBlocks);
268 LOG_DEBUG(logger,
"Built GLSL program id: %d (active attribs=%d, uniforms=%d, blocks=%d)", programId, activeAttributes, activeUniforms, activeUniformBlocks);
270 buffer.resize(attributeMaxLength);
271 for (
int i = 0; i < activeAttributes; ++i) {
274 GLenum type = GL_INVALID_ENUM;
276 glGetActiveAttrib(programId,
static_cast<GLsizei
>(i),
static_cast<GLsizei
>(buffer.size()), &length, &size, &type, buffer.data());
278 const char* beg = buffer.data();
279 const char* end = beg + attributeMaxLength;
283 const char* ptr = beg;
284 while ((ptr < end) && (*ptr !=
'\0') && ((*ptr <
'0') || (
'9' < *ptr))) { ptr++; }
299 LOG_ERROR(logger,
"Unrecognized attribute name '%.*s'", StringViewFormat(semanticName));
300 glDeleteProgram(programId);
303 if (semantic == ElementSemantic::Semantic_Size) {
305 LOG_DEBUG(logger,
"Attrib -: name=%s type=%s", buffer.data(), getGLTypeName(type));
311 program.attributeSemantic[i].semantic = uint8_t(semantic);
315 while ((ptr < end) && (*ptr !=
'\0') && (
'0' <= *ptr) && (*ptr <=
'9')) { slot = 10 * slot + uint32_t(*ptr -
'0'); ptr++; }
317 LOG_ERROR(logger,
"Invalid attribute slot index %u in name '%s'", slot, buffer.data());
318 glDeleteProgram(programId);
322 LOG_ERROR(logger,
"Unrecognized characters after attribute index in name '%s'", buffer.data());
323 glDeleteProgram(programId);
326 program.attributeSemantic[i].slot = uint8_t(slot);
328 const GLint location = glGetAttribLocation(programId, buffer.data());
329 if (location < 0 || 0xffff < location) {
330 LOG_ERROR(logger,
"Invalid attribute location %d, assumptions broken", location);
331 glDeleteProgram(programId);
334 program.attributeLocation[i] = location;
337 LOG_DEBUG(logger,
"Attrib %d: name=%s type=%s semantic=%.*s::%u", location, buffer.data(), getGLTypeName(type), StringViewFormat(getElementSemanticName(semantic)), slot);
340 program.activeAttributes =
static_cast<uint8_t
>(std::max(0, activeAttributes));
342 GLint uniformMaxLength = 0;
343 glGetProgramiv(programId, GL_ACTIVE_UNIFORM_MAX_LENGTH, &uniformMaxLength);
347 GLint currentProgram = 0;
348 glGetIntegerv(GL_CURRENT_PROGRAM, ¤tProgram);
349 glUseProgram(program.programId);
351 size_t allocatedTexUnits = 0;
352 buffer.resize(uniformMaxLength);
353 for (
int i = 0; i < activeUniforms; i++) {
356 GLenum type = GL_INVALID_ENUM;
357 glGetActiveUniform(programId,
static_cast<GLsizei
>(i),
static_cast<GLsizei
>(buffer.size()), &length, &size, &type, buffer.data());
359 const GLint location = glGetUniformLocation(programId, buffer.data());
361 bool isActiveSampler =
false;
363 case GL_SAMPLER_2D: isActiveSampler =
true;
break;
364 case GL_SAMPLER_3D: isActiveSampler =
true;
break;
365 case GL_SAMPLER_CUBE: isActiveSampler =
true;
break;
366 case GL_SAMPLER_2D_SHADOW: isActiveSampler =
true;
break;
367 case GL_SAMPLER_2D_ARRAY: isActiveSampler =
true;
break;
368 case GL_SAMPLER_2D_ARRAY_SHADOW: isActiveSampler =
true;
break;
369 case GL_SAMPLER_CUBE_SHADOW: isActiveSampler =
true;
break;
370 case GL_INT_SAMPLER_2D: isActiveSampler =
true;
break;
371 case GL_INT_SAMPLER_3D: isActiveSampler =
true;
break;
372 case GL_INT_SAMPLER_CUBE: isActiveSampler =
true;
break;
373 case GL_INT_SAMPLER_2D_ARRAY: isActiveSampler =
true;
break;
374 case GL_UNSIGNED_INT_SAMPLER_2D: isActiveSampler =
true;
break;
375 case GL_UNSIGNED_INT_SAMPLER_3D: isActiveSampler =
true;
break;
376 case GL_UNSIGNED_INT_SAMPLER_CUBE: isActiveSampler =
true;
break;
377 case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: isActiveSampler =
true;
break;
380 if (isActiveSampler) {
383 if (allocatedTexUnits == OpenGLES30::maxTexUnits) {
384 LOG_ERROR(logger,
"Active texture units exceed OpenGLES30::maxTexUnits");
387 unit = allocatedTexUnits++;
389 glUniform1i(location, GLint(unit));
392 LOG_DEBUG(logger,
"Uniform %d: name=%s type=%s sampler unit=%zu", location, buffer.data(), getGLTypeName(type), unit);
399 LOG_DEBUG(logger,
"Uniform %d: name=%s type=%s", location, buffer.data(), getGLTypeName(type));
402 assert(allocatedTexUnits <= OpenGLES30::maxTexUnits);
403 program.activeTextureUnits =
static_cast<uint8_t
>(allocatedTexUnits);
411 GLuint nextBinding = 1;
412 std::memset(&program.uniformBlockBindings[0], 0,
sizeof(program.uniformBlockBindings));
413 for (GLuint activeBlockIndex = 0; GLint(activeBlockIndex) < activeUniformBlocks; activeBlockIndex++) {
414 char uniformBlockNameChars[256] = {};
415 GLsizei uniformBlockNameLength = 0;
416 glGetActiveUniformBlockName(programId, activeBlockIndex, GLsizei(
sizeof(uniformBlockNameChars)), &uniformBlockNameLength, uniformBlockNameChars);
417 StringView uniformBlockName(uniformBlockNameChars, uniformBlockNameLength);
420 glGetActiveUniformBlockiv(programId, activeBlockIndex, GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER, &vRef);
423 glGetActiveUniformBlockiv(programId, activeBlockIndex, GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER, &fRef);
427 if (!uniformBlockName.empty() && (vRef != 0 || fRef != 0)) {
429 binding = nextBinding++;
431 size_t nameHash =
Cogs::hash(uniformBlockName);
433 program.uniformBlockBindings[binding].nameHash = nameHash;
435 glUniformBlockBinding(programId, activeBlockIndex, binding);
439 glGetActiveUniformBlockiv(programId, activeBlockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &dataSize);
441 LOG_DEBUG(logger,
"block %d: bind=%d size=%5d %c%c name='%.*s'", activeBlockIndex, binding, dataSize, vRef ?
'V' :
' ', fRef ?
'F' :
' ', StringViewFormat(uniformBlockName));
446 LOG_DEBUG(logger,
"Loaded GLSL program id: %d", programId);
449 glUseProgram(currentProgram);
450 return effects.addResource(program);
455 LOG_ERROR(logger,
"loadComputeEffect: compute shaders not supported");
461 LOG_ERROR(logger,
"loadComputeEffect: compute shaders not supported");
467 if (!HandleIsValid(effectHandle) || name.
empty()) {
473 for (
size_t i = 0; i < effect.activeTextureUnits; i++) {
474 if (effect.textureSlots[i].nameHash == nameHash) {
483 const char suffix[] =
"Sampler";
484 constexpr size_t suffixLength =
sizeof(suffix) - 1;
485 if (HandleIsValid(effectHandle)) {
489 if ((suffixLength < name.
length()) &&
490 (std::memcmp(name.
data() + name.
size() - suffixLength, suffix, suffixLength) == 0))
493 for (
size_t i = 0; i < effect.activeTextureUnits; i++) {
494 if (effect.textureSlots[i].nameHash == nameHash) {
506 this->context = context;
507 EffectsCommon::initialize(buffers);
514 const GLint programId = effects[effectHandle].programId;
515 const GLint location = glGetUniformLocation(programId, name.
data());
527 if (!HandleIsValid(effectHandle)) {
528 LOG_ERROR_ONCE(logger,
"getConstantBufferBinding: Invalid effect handle");
534 for (
size_t index = 0; index < OpenGLES30::maxUniformBuffers; index++) {
535 if (effect.uniformBlockBindings[index].nameHash == nameHash) {
555 if (!hsSource.content.empty() || !dsSource.content.empty()) {
556 LOG_ERROR(logger,
"load: Tessellation shaders are not supported.");
559 if (!gsSource.content.empty()) {
560 LOG_ERROR(logger,
"load: Geometry shaders are not supported.");
563 if (!(vsEntryPoint.
empty() || vsEntryPoint ==
"main") ||
564 !(hsEntryPoint.
empty() || hsEntryPoint ==
"main") ||
565 !(dsEntryPoint.
empty() || dsEntryPoint ==
"main") ||
566 !(gsEntryPoint.
empty() || gsEntryPoint ==
"main") ||
567 !(psEntryPoint.
empty() || psEntryPoint ==
"main"))
569 LOG_ERROR(logger,
"load: Shader entry points not named main are not supported.");
573 return loadEffect(vsSource.content.c_str(), psSource.content.c_str(), desc.definitions, desc.flags);
Log implementation class.
Provides a weakly referenced view over the contents of a string.
constexpr const char * data() const noexcept
Get the sequence of characters referenced by the string view.
constexpr size_t size() const noexcept
Get the size of the string.
constexpr size_t length() const noexcept
Get the length of the string.
constexpr bool empty() const noexcept
Check if the string is empty.
constexpr StringView substr(size_t offset, size_t count=NoPosition) const noexcept
Get the given sub string.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Contains all Cogs related functionality.
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
std::pair< std::string, std::string > PreprocessorDefinition
Preprocessor definition.
ElementSemantic
Element semantics used to map data to the shader stage.
@ Position
Position semantic.
@ Tangent
Tangent semantic.
@ InstanceMatrix
Instance matrix semantic.
@ InstanceVector
Instance vector semantic.
@ TextureCoordinate
Texture coordinate semantic.
std::vector< PreprocessorDefinition > PreprocessorDefinitions
A set of preprocessor definitions.
void setEffect(EffectHandle handle) override
Set the current effect.
Contains an effect description used to load a single effect.
EEffectFlags
Effect source flags.
@ LogShaderInfo
Log detailed info about shaders.
SamplerStateBindingHandle getSamplerStateBinding(EffectHandle effectHandle, const StringView &name, const unsigned int slot) override
Get a handle to a sampler state object binding, mapping how to bind the sampler state to the given ef...
EffectVariableHandle getEffectVariable(EffectHandle effectHandle, const StringView &name) override
Get a handle to the variable with the given name in the effect with the given effectHandle.
virtual EffectHandle loadComputeEffect(const StringView &fileName, EffectFlags::EEffectFlags effectFlags) override
Load the compute shader with the given file name and create an effect.
void releaseResources()
Release all allocated effect resources.
TextureBindingHandle getTextureBinding(EffectHandle effectHandle, const StringView &name, const unsigned int slot) override
Get a handle to a texture object binding, mapping how to bind textures to the given effect.
ConstantBufferBindingHandle getConstantBufferBinding(EffectHandle effectHandle, const StringView &name) override
Get a handle to a constant buffer binding, mapping how to bind a constant buffer to the given effect.
void releaseEffect(EffectHandle handle)
Release the effect with the given handle, freeing all resources generated during program loading.
static const Handle_t NoHandle
Represents a handle to nothing.
static const Handle_t InvalidHandle
Represents an invalid handle.