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);
452 program.readyAtFrame = currentFrame + delayFrames;
453 if (program.readyAtFrame > allEffectsReadyFrame) allEffectsReadyFrame = program.readyAtFrame;
456 return effects.addResource(program);
461 LOG_ERROR(logger,
"loadComputeEffect: compute shaders not supported");
467 LOG_ERROR(logger,
"loadComputeEffect: compute shaders not supported");
473 if (!HandleIsValid(effectHandle) || name.
empty()) {
479 for (
size_t i = 0; i < effect.activeTextureUnits; i++) {
480 if (effect.textureSlots[i].nameHash == nameHash) {
489 const char suffix[] =
"Sampler";
490 constexpr size_t suffixLength =
sizeof(suffix) - 1;
491 if (HandleIsValid(effectHandle)) {
495 if ((suffixLength < name.
length()) &&
496 (std::memcmp(name.
data() + name.
size() - suffixLength, suffix, suffixLength) == 0))
499 for (
size_t i = 0; i < effect.activeTextureUnits; i++) {
500 if (effect.textureSlots[i].nameHash == nameHash) {
510void Cogs::EffectsGLES30::initialize(
ContextGLES30* context, uint32_t delayFrames)
512 this->context = context;
513 this->delayFrames = delayFrames;
514 EffectsCommon::initialize(buffers);
529 const GLint programId = effects[effectHandle].programId;
530 const GLint location = glGetUniformLocation(programId, name.
data());
542 if (!HandleIsValid(effectHandle)) {
543 LOG_ERROR_ONCE(logger,
"getConstantBufferBinding: Invalid effect handle");
549 for (
size_t index = 0; index < OpenGLES30::maxUniformBuffers; index++) {
550 if (effect.uniformBlockBindings[index].nameHash == nameHash) {
570 if (!hsSource.content.empty() || !dsSource.content.empty()) {
571 LOG_ERROR(logger,
"load: Tessellation shaders are not supported.");
574 if (!gsSource.content.empty()) {
575 LOG_ERROR(logger,
"load: Geometry shaders are not supported.");
578 if (!(vsEntryPoint.
empty() || vsEntryPoint ==
"main") ||
579 !(hsEntryPoint.
empty() || hsEntryPoint ==
"main") ||
580 !(dsEntryPoint.
empty() || dsEntryPoint ==
"main") ||
581 !(gsEntryPoint.
empty() || gsEntryPoint ==
"main") ||
582 !(psEntryPoint.
empty() || psEntryPoint ==
"main"))
584 LOG_ERROR(logger,
"load: Shader entry points not named main are not supported.");
588 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.
ResourceStatus
Status of an asynchronously loaded resource, such as an effect or a pipeline.
@ Pending
The resource is still loading.
@ Error
The resource failed to load.
@ Ready
The resource has loaded successfully and is ready for use.
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.
ResourceStatus checkEffect(EffectHandle effectHandle) override
Check the load status of the effect with the given effectHandle.
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.