Initial commit
This commit is contained in:
393
graphics/opengl/context.cpp
Normal file
393
graphics/opengl/context.cpp
Normal file
@@ -0,0 +1,393 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#define GLAD_GL_IMPLEMENTATION
|
||||
// sscanf_s is used by glad on MSVC only
|
||||
// we can't know before config.h is loaded that GLAD will be used
|
||||
// but at this time it will be too late to allow sscanf_s
|
||||
#ifdef _MSC_VER
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_sscanf_s
|
||||
#endif
|
||||
|
||||
#include "graphics/opengl/context.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/str.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/tokenizer.h"
|
||||
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
|
||||
#if defined(USE_OPENGL)
|
||||
|
||||
#ifdef USE_GLAD
|
||||
static GLADapiproc loadFunc(const char *name) {
|
||||
return (GLADapiproc)g_system->getOpenGLProcAddress(name);
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
DECLARE_SINGLETON(OpenGL::Context);
|
||||
}
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
Context::Context() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void Context::reset() {
|
||||
type = kContextNone;
|
||||
maxTextureSize = 0;
|
||||
|
||||
majorVersion = 0;
|
||||
minorVersion = 0;
|
||||
glslVersion = 0;
|
||||
|
||||
NPOTSupported = false;
|
||||
imagingSupported = false;
|
||||
shadersSupported = false;
|
||||
enginesShadersSupported = false;
|
||||
multitextureSupported = false;
|
||||
framebufferObjectSupported = false;
|
||||
framebufferObjectMultisampleSupported = false;
|
||||
multisampleMaxSamples = -1;
|
||||
bgraSupported = false;
|
||||
packedPixelsSupported = false;
|
||||
packedDepthStencilSupported = false;
|
||||
unpackSubImageSupported = false;
|
||||
OESDepth24 = false;
|
||||
textureEdgeClampSupported = false;
|
||||
textureBorderClampSupported = false;
|
||||
textureMirrorRepeatSupported = false;
|
||||
textureMaxLevelSupported = false;
|
||||
textureLookupPrecision = 0;
|
||||
}
|
||||
|
||||
void Context::initialize(ContextType contextType) {
|
||||
// Initialize default state.
|
||||
reset();
|
||||
if (contextType == kContextNone) {
|
||||
return;
|
||||
}
|
||||
|
||||
type = contextType;
|
||||
|
||||
#ifdef USE_GLAD
|
||||
int gladVersion;
|
||||
switch (type) {
|
||||
case kContextGL:
|
||||
gladVersion = gladLoadGL(loadFunc);
|
||||
break;
|
||||
|
||||
case kContextGLES:
|
||||
gladVersion = gladLoadGLES1(loadFunc);
|
||||
break;
|
||||
|
||||
case kContextGLES2:
|
||||
gladVersion = gladLoadGLES2(loadFunc);
|
||||
break;
|
||||
|
||||
default:
|
||||
gladVersion = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
majorVersion = GLAD_VERSION_MAJOR(gladVersion);
|
||||
minorVersion = GLAD_VERSION_MINOR(gladVersion);
|
||||
|
||||
if (!gladVersion) {
|
||||
// If gladVersion is 0 it means that loading failed and glad didn't set up anything
|
||||
error("Couldn't initialize OpenGL");
|
||||
}
|
||||
#else
|
||||
const char *verString = (const char *)glGetString(GL_VERSION);
|
||||
if (!verString) {
|
||||
majorVersion = minorVersion = 0;
|
||||
int errorCode = glGetError();
|
||||
warning("Could not fetch GL_VERSION: %d", errorCode);
|
||||
return;
|
||||
} else if (type == kContextGL) {
|
||||
// OpenGL version number is either of the form major.minor or major.minor.release,
|
||||
// where the numbers all have one or more digits
|
||||
if (sscanf(verString, "%d.%d", &majorVersion, &minorVersion) != 2) {
|
||||
majorVersion = minorVersion = 0;
|
||||
warning("Could not parse GL version '%s'", verString);
|
||||
}
|
||||
} else if (type == kContextGLES) {
|
||||
// The form of the string is "OpenGL ES-<profile> <major>.<minor>",
|
||||
// where <profile> is either "CM" (Common) or "CL" (Common-Lite),
|
||||
// and <major> and <minor> are integers.
|
||||
char profile[3];
|
||||
if (sscanf(verString, "OpenGL ES-%2s %d.%d", profile,
|
||||
&majorVersion, &minorVersion) != 3) {
|
||||
majorVersion = minorVersion = 0;
|
||||
warning("Could not parse GL ES version '%s'", verString);
|
||||
}
|
||||
} else if (type == kContextGLES2) {
|
||||
// The version is of the form
|
||||
// OpenGL<space>ES<space><version number><space><vendor-specific information>
|
||||
// version number format is not defined
|
||||
// There is only OpenGL ES 2.0 anyway
|
||||
if (sscanf(verString, "OpenGL ES %d.%d", &majorVersion, &minorVersion) != 2) {
|
||||
minorVersion = 0;
|
||||
if (sscanf(verString, "OpenGL ES %d ", &majorVersion) != 1) {
|
||||
majorVersion = 0;
|
||||
warning("Could not parse GL ES 2 version '%s'", verString);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
glslVersion = getGLSLVersion();
|
||||
|
||||
// Obtain maximum texture size.
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint *)&maxTextureSize);
|
||||
|
||||
const char *extString = (const char *)glGetString(GL_EXTENSIONS);
|
||||
if (!extString) {
|
||||
extString = "";
|
||||
}
|
||||
|
||||
bool EXTFramebufferMultisample = false;
|
||||
bool EXTFramebufferBlit = false;
|
||||
|
||||
Common::StringTokenizer tokenizer(extString, " ");
|
||||
while (!tokenizer.empty()) {
|
||||
Common::String token = tokenizer.nextToken();
|
||||
|
||||
if (token == "GL_ARB_texture_non_power_of_two" || token == "GL_OES_texture_npot") {
|
||||
NPOTSupported = true;
|
||||
} else if (token == "GL_ARB_imaging") {
|
||||
imagingSupported = true;
|
||||
} else if (token == "GL_ARB_multitexture") {
|
||||
multitextureSupported = true;
|
||||
} else if (token == "GL_ARB_framebuffer_object") {
|
||||
framebufferObjectSupported = true;
|
||||
} else if (token == "GL_EXT_bgra" || token == "GL_EXT_texture_format_BGRA8888") {
|
||||
bgraSupported = true;
|
||||
} else if (token == "GL_EXT_packed_pixels" || token == "GL_APPLE_packed_pixels") {
|
||||
packedPixelsSupported = true;
|
||||
} else if (token == "GL_EXT_packed_depth_stencil" || token == "GL_OES_packed_depth_stencil") {
|
||||
packedDepthStencilSupported = true;
|
||||
} else if (token == "GL_EXT_unpack_subimage") {
|
||||
unpackSubImageSupported = true;
|
||||
} else if (token == "GL_EXT_framebuffer_multisample") {
|
||||
EXTFramebufferMultisample = true;
|
||||
} else if (token == "GL_EXT_framebuffer_blit") {
|
||||
EXTFramebufferBlit = true;
|
||||
} else if (token == "GL_OES_depth24") {
|
||||
OESDepth24 = true;
|
||||
} else if (token == "GL_SGIS_texture_edge_clamp") {
|
||||
textureEdgeClampSupported = true;
|
||||
} else if (token == "GL_ARB_texture_border_clamp" || token == "GL_SGIS_texture_border_clamp" || token == "GL_OES_texture_border_clamp" ||
|
||||
token == "GL_EXT_texture_border_clamp" || token == "GL_NV_texture_border_clamp") {
|
||||
textureBorderClampSupported = true;
|
||||
} else if (token == "GL_ARB_texture_mirrored_repeat" || token == "GL_OES_texture_mirrored_repeat" || token == "GL_IBM_texture_mirrored_repeat") {
|
||||
textureMirrorRepeatSupported = true;
|
||||
} else if (token == "GL_SGIS_texture_lod" || token == "GL_APPLE_texture_max_level") {
|
||||
textureMaxLevelSupported = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (type == kContextGLES2) {
|
||||
// OGLES2 on AmigaOS reports GLSL version as 0.9 but we do what is needed to make it work
|
||||
// so let's pretend it supports 1.00
|
||||
#if defined(__amigaos4__)
|
||||
if (glslVersion < 100) {
|
||||
glslVersion = 100;
|
||||
}
|
||||
#endif
|
||||
// GLES2 always has (limited) NPOT support.
|
||||
NPOTSupported = true;
|
||||
|
||||
// GLES2 always has imaging support
|
||||
imagingSupported = true;
|
||||
|
||||
// GLES2 always has shader support.
|
||||
shadersSupported = true;
|
||||
// GLES2 should always have GLSL ES 1.00 support but let's make sure
|
||||
enginesShadersSupported = (glslVersion >= 100);
|
||||
|
||||
// GLES2 always has multi texture support.
|
||||
multitextureSupported = true;
|
||||
|
||||
// GLES2 always has FBO support.
|
||||
framebufferObjectSupported = true;
|
||||
|
||||
packedPixelsSupported = true;
|
||||
textureEdgeClampSupported = true;
|
||||
textureMirrorRepeatSupported = true;
|
||||
|
||||
// OpenGL ES 3.0 and later adds multisample FBOs, and always has the following extensions
|
||||
if (isGLVersionOrHigher(3, 0)) {
|
||||
framebufferObjectMultisampleSupported = true;
|
||||
packedDepthStencilSupported = true;
|
||||
textureMaxLevelSupported = true;
|
||||
unpackSubImageSupported = true;
|
||||
OESDepth24 = true;
|
||||
}
|
||||
// OpenGL ES 3.2 and later always has texture border clamp support
|
||||
if (isGLVersionOrHigher(3, 2)) {
|
||||
textureBorderClampSupported = true;
|
||||
}
|
||||
|
||||
// In GLES2, texture lookup is done using lowp (and mediump is not always available)
|
||||
GLint range[2];
|
||||
GLint precision = 0;
|
||||
glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_LOW_FLOAT, range, &precision);
|
||||
textureLookupPrecision = precision;
|
||||
|
||||
debug(5, "OpenGL: GLES2 context initialized");
|
||||
} else if (type == kContextGLES) {
|
||||
// GLES doesn't support shaders natively
|
||||
|
||||
// ScummVM does not support multisample FBOs with GLES for now
|
||||
framebufferObjectMultisampleSupported = false;
|
||||
|
||||
// GLES always has imaging support
|
||||
imagingSupported = true;
|
||||
|
||||
packedPixelsSupported = true;
|
||||
textureEdgeClampSupported = true;
|
||||
// No border clamping in GLES
|
||||
// No mirror repeat in GLES
|
||||
// Precision is irrelevant (shaders) in GLES
|
||||
|
||||
debug(5, "OpenGL: GLES context initialized");
|
||||
} else if (type == kContextGL) {
|
||||
// Official support of shaders starts with version 110
|
||||
// Older versions didn't support the #version directive and were only available through
|
||||
// ARB extensions which we removed support for
|
||||
shadersSupported = glslVersion >= 110;
|
||||
|
||||
// In GL mode engines need GLSL 1.20
|
||||
enginesShadersSupported = glslVersion >= 120;
|
||||
|
||||
// Desktop GL always has unpack sub-image support
|
||||
unpackSubImageSupported = true;
|
||||
|
||||
framebufferObjectMultisampleSupported = EXTFramebufferMultisample && EXTFramebufferBlit;
|
||||
|
||||
// OpenGL 1.2 and later always has packed pixels, texture edge clamp and texture max level support
|
||||
if (isGLVersionOrHigher(1, 2)) {
|
||||
bgraSupported = true;
|
||||
packedPixelsSupported = true;
|
||||
textureEdgeClampSupported = true;
|
||||
textureMaxLevelSupported = true;
|
||||
}
|
||||
// OpenGL 1.3 adds texture border clamp support and mandatory imaging support
|
||||
if (isGLVersionOrHigher(1, 3)) {
|
||||
imagingSupported = true;
|
||||
textureBorderClampSupported = true;
|
||||
}
|
||||
// OpenGL 1.4 adds texture mirror repeat support
|
||||
if (isGLVersionOrHigher(1, 4)) {
|
||||
textureMirrorRepeatSupported = true;
|
||||
}
|
||||
|
||||
// In OpenGL precision is always enough
|
||||
textureLookupPrecision = UINT_MAX;
|
||||
|
||||
debug(5, "OpenGL: GL context initialized");
|
||||
} else {
|
||||
warning("OpenGL: Unknown context initialized");
|
||||
}
|
||||
|
||||
if (framebufferObjectMultisampleSupported) {
|
||||
glGetIntegerv(GL_MAX_SAMPLES, (GLint *)&multisampleMaxSamples);
|
||||
}
|
||||
|
||||
const char *glslVersionString = glslVersion ? (const char *)glGetString(GL_SHADING_LANGUAGE_VERSION) : "";
|
||||
|
||||
// Log features supported by GL context.
|
||||
debug(5, "OpenGL version: %s", glGetString(GL_VERSION));
|
||||
debug(5, "OpenGL vendor: %s", glGetString(GL_VENDOR));
|
||||
debug(5, "OpenGL renderer: %s", glGetString(GL_RENDERER));
|
||||
debug(5, "OpenGL: version %d.%d", majorVersion, minorVersion);
|
||||
debug(5, "OpenGL: GLSL version string: %s", glslVersionString ? glslVersionString : "<none>");
|
||||
debug(5, "OpenGL: GLSL version: %d", glslVersion);
|
||||
debug(5, "OpenGL: Max texture size: %d", maxTextureSize);
|
||||
debug(5, "OpenGL: NPOT texture support: %d", NPOTSupported);
|
||||
debug(5, "OpenGL: Imaging support: %d", imagingSupported);
|
||||
debug(5, "OpenGL: Shader support: %d", shadersSupported);
|
||||
debug(5, "OpenGL: Shader support for engines: %d", enginesShadersSupported);
|
||||
debug(5, "OpenGL: Multitexture support: %d", multitextureSupported);
|
||||
debug(5, "OpenGL: FBO support: %d", framebufferObjectSupported);
|
||||
debug(5, "OpenGL: Multisample FBO support: %d", framebufferObjectMultisampleSupported);
|
||||
debug(5, "OpenGL: Multisample max number: %d", multisampleMaxSamples);
|
||||
debug(5, "OpenGL: BGRA support: %d", bgraSupported);
|
||||
debug(5, "OpenGL: Packed pixels support: %d", packedPixelsSupported);
|
||||
debug(5, "OpenGL: Packed depth stencil support: %d", packedDepthStencilSupported);
|
||||
debug(5, "OpenGL: Unpack subimage support: %d", unpackSubImageSupported);
|
||||
debug(5, "OpenGL: OpenGL ES depth 24 support: %d", OESDepth24);
|
||||
debug(5, "OpenGL: Texture edge clamping support: %d", textureEdgeClampSupported);
|
||||
debug(5, "OpenGL: Texture border clamping support: %d", textureBorderClampSupported);
|
||||
debug(5, "OpenGL: Texture mirror repeat support: %d", textureMirrorRepeatSupported);
|
||||
debug(5, "OpenGL: Texture max level support: %d", textureMaxLevelSupported);
|
||||
debug(5, "OpenGL: Texture lookup precision: %d", textureLookupPrecision);
|
||||
}
|
||||
|
||||
int Context::getGLSLVersion() const {
|
||||
#if USE_FORCED_GLES
|
||||
return 0;
|
||||
#else
|
||||
// No shader support in GLES
|
||||
if (type == kContextGLES) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// No shader support in OpenGL 1.x
|
||||
if (type == kContextGL && !isGLVersionOrHigher(2, 0)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *glslVersionString = (const char *)glGetString(GL_SHADING_LANGUAGE_VERSION);
|
||||
if (!glslVersionString) {
|
||||
warning("Could not get GLSL version");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Search for the first digit in the version string and parse from there
|
||||
const char *glslVersionStringNum;
|
||||
for (glslVersionStringNum = glslVersionString; *glslVersionStringNum != '\0'; glslVersionStringNum++) {
|
||||
if (*glslVersionStringNum >= '0' &&
|
||||
*glslVersionStringNum <= '9') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Here *glslVersionStringNum is either a digit or a NUL character
|
||||
|
||||
int glslMajorVersion, glslMinorVersion;
|
||||
if (sscanf(glslVersionStringNum, "%d.%d", &glslMajorVersion, &glslMinorVersion) != 2) {
|
||||
warning("Could not parse GLSL version '%s' extracted from '%s'", glslVersionStringNum, glslVersionString);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return glslMajorVersion * 100 + glslMinorVersion;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // End of namespace OpenGL
|
||||
|
||||
#endif
|
||||
145
graphics/opengl/context.h
Normal file
145
graphics/opengl/context.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRAPHICS_OPENGL_CONTEXT_H
|
||||
#define GRAPHICS_OPENGL_CONTEXT_H
|
||||
|
||||
#include "common/singleton.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
enum ContextType {
|
||||
kContextNone,
|
||||
kContextGL,
|
||||
kContextGLES,
|
||||
kContextGLES2
|
||||
};
|
||||
|
||||
/**
|
||||
* Description structure of the OpenGL (ES) context.
|
||||
*/
|
||||
class Context : public Common::Singleton<Context> {
|
||||
public:
|
||||
Context();
|
||||
|
||||
/**
|
||||
* Initialize the context description from currently active context.
|
||||
*
|
||||
* The extensions and features are marked as available according
|
||||
* to the current context capabilities.
|
||||
*/
|
||||
void initialize(ContextType contextType);
|
||||
|
||||
/**
|
||||
* Reset context.
|
||||
*
|
||||
* This marks all extensions as unavailable.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/** The type of the active context. */
|
||||
ContextType type;
|
||||
|
||||
/** Helper function for checking the GL version supported by the context. */
|
||||
inline bool isGLVersionOrHigher(int major, int minor) const {
|
||||
return ((majorVersion > major) || ((majorVersion == major) && (minorVersion >= minor)));
|
||||
}
|
||||
|
||||
/** The GL version supported by the context. */
|
||||
int majorVersion, minorVersion;
|
||||
|
||||
/** The GLSL version supported by the context */
|
||||
int glslVersion;
|
||||
|
||||
/** The maximum texture size supported by the context. */
|
||||
int maxTextureSize;
|
||||
|
||||
/** Whether GL_ARB_texture_non_power_of_two is available or not. */
|
||||
bool NPOTSupported;
|
||||
|
||||
/** Whether GL_ARB_imaging is available or not. */
|
||||
bool imagingSupported;
|
||||
|
||||
/** Whether shader support is available or not. */
|
||||
bool shadersSupported;
|
||||
|
||||
/** Whether shader support is good enough for engines or not. */
|
||||
bool enginesShadersSupported;
|
||||
|
||||
/** Whether multi texture support is available or not. */
|
||||
bool multitextureSupported;
|
||||
|
||||
/** Whether FBO support is available or not. */
|
||||
bool framebufferObjectSupported;
|
||||
|
||||
/** Whether multisample FBO support is available or not */
|
||||
bool framebufferObjectMultisampleSupported;
|
||||
|
||||
/**
|
||||
* Contains the maximum number of supported multisample samples
|
||||
* if multisample FBOs are supported. Contains -1 otherwise.
|
||||
*/
|
||||
int multisampleMaxSamples;
|
||||
|
||||
/** Whether BGRA support is available or not. */
|
||||
bool bgraSupported;
|
||||
|
||||
/** Whether packed pixels support is available or not. */
|
||||
bool packedPixelsSupported;
|
||||
|
||||
/** Whether packing the depth and stencil buffers is possible or not. */
|
||||
bool packedDepthStencilSupported;
|
||||
|
||||
/** Whether specifying a pitch when uploading to textures is available or not */
|
||||
bool unpackSubImageSupported;
|
||||
|
||||
/** Whether depth component 24 is supported or not */
|
||||
bool OESDepth24;
|
||||
|
||||
/** Whether texture coordinate edge clamping is available or not. */
|
||||
bool textureEdgeClampSupported;
|
||||
|
||||
/** Whether texture coordinate border clamping is available or not. */
|
||||
bool textureBorderClampSupported;
|
||||
|
||||
/** Whether texture coordinate mirrored repeat is available or not. */
|
||||
bool textureMirrorRepeatSupported;
|
||||
|
||||
/** Whether texture max level is available or not. */
|
||||
bool textureMaxLevelSupported;
|
||||
|
||||
/** Texture lookup result precision. */
|
||||
unsigned int textureLookupPrecision;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Returns the native GLSL version supported by the driver.
|
||||
* This does NOT take shaders ARB extensions into account.
|
||||
*/
|
||||
int getGLSLVersion() const;
|
||||
};
|
||||
|
||||
} // End of namespace OpenGL
|
||||
|
||||
/** Shortcut for accessing the active OpenGL context. */
|
||||
#define OpenGLContext OpenGL::Context::instance()
|
||||
|
||||
#endif
|
||||
77
graphics/opengl/debug.cpp
Normal file
77
graphics/opengl/debug.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "graphics/opengl/debug.h"
|
||||
|
||||
#include "common/str.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
|
||||
#if defined(USE_OPENGL)
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
namespace {
|
||||
Common::String getGLErrStr(GLenum error) {
|
||||
switch (error) {
|
||||
case GL_INVALID_ENUM:
|
||||
return "GL_INVALID_ENUM";
|
||||
case GL_INVALID_VALUE:
|
||||
return "GL_INVALID_VALUE";
|
||||
case GL_INVALID_OPERATION:
|
||||
return "GL_INVALID_OPERATION";
|
||||
case GL_STACK_OVERFLOW:
|
||||
return "GL_STACK_OVERFLOW";
|
||||
case GL_STACK_UNDERFLOW:
|
||||
return "GL_STACK_UNDERFLOW";
|
||||
case GL_OUT_OF_MEMORY:
|
||||
return "GL_OUT_OF_MEMORY";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return Common::String::format("(Unknown GL error code 0x%X)", error);
|
||||
}
|
||||
} // End of anonymous namespace
|
||||
|
||||
void clearGLError() {
|
||||
GLenum error;
|
||||
|
||||
while ((error = glGetError()) != GL_NO_ERROR)
|
||||
;
|
||||
}
|
||||
|
||||
bool checkGLError(const char *expr, const char *file, int line) {
|
||||
bool ret = false;
|
||||
GLenum error;
|
||||
|
||||
while ((error = glGetError()) != GL_NO_ERROR) {
|
||||
// We cannot use error here because we do not know whether we have a
|
||||
// working screen or not.
|
||||
warning("GL ERROR: %s on %s (%s:%d)", getGLErrStr(error).c_str(), expr, file, line);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
} // End of namespace OpenGL
|
||||
|
||||
#endif
|
||||
57
graphics/opengl/debug.h
Normal file
57
graphics/opengl/debug.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_GRAPHICS_OPENGL_DEBUG_H
|
||||
#define BACKENDS_GRAPHICS_OPENGL_DEBUG_H
|
||||
|
||||
#include "graphics/opengl/context.h"
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#define OPENGL_DEBUG
|
||||
#endif
|
||||
|
||||
namespace OpenGL {
|
||||
void clearGLError();
|
||||
bool checkGLError(const char *expr, const char *file, int line);
|
||||
} // End of namespace OpenGL
|
||||
|
||||
#define GL_WRAP_CHECK(err, call, name) do { OpenGL::clearGLError(); (call); err = OpenGL::checkGLError(#name, __FILE__, __LINE__); } while (false)
|
||||
#ifdef OPENGL_DEBUG
|
||||
#define GL_WRAP_DEBUG(call, name) do { OpenGL::clearGLError(); (call); OpenGL::checkGLError(#name, __FILE__, __LINE__); } while (false)
|
||||
#else
|
||||
#define GL_WRAP_DEBUG(call, name) do { (call); } while (false)
|
||||
#endif
|
||||
|
||||
// This macro will set boolean err to true if there was an error and display them in the console
|
||||
#define GL_CALL_CHECK(err, x) GL_WRAP_CHECK(err, x, x)
|
||||
// This macro will check for errors and display them in the console only when OPENGL_DEBUG is enabled
|
||||
#define GL_CALL(x) GL_WRAP_DEBUG(x, x)
|
||||
// This macro will execute the call only when the context is valid and will check errors only when OPENGL_DEBUG is enabled
|
||||
#define GL_CALL_SAFE(func, params) \
|
||||
do { \
|
||||
if (OpenGLContext.type != kContextNone) { \
|
||||
GL_CALL(func params); \
|
||||
} \
|
||||
} while (0)
|
||||
// This macro will assign result of x to var and check for errors when OPENGL_DEBUG is enabled
|
||||
#define GL_ASSIGN(var, x) GL_WRAP_DEBUG(var = x, x)
|
||||
|
||||
#endif
|
||||
14196
graphics/opengl/glad.h
Normal file
14196
graphics/opengl/glad.h
Normal file
File diff suppressed because it is too large
Load Diff
519
graphics/opengl/shader.cpp
Normal file
519
graphics/opengl/shader.cpp
Normal file
@@ -0,0 +1,519 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/config-manager.h"
|
||||
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
|
||||
#if defined(USE_OPENGL) && !USE_FORCED_GLES
|
||||
|
||||
#include "graphics/opengl/shader.h"
|
||||
|
||||
#include "graphics/opengl/context.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
static const char *const compatVertex =
|
||||
"#if defined(GL_ES)\n"
|
||||
"#define ROUND(x) (sign(x) * floor(abs(x) + .5))\n"
|
||||
"#define in attribute\n"
|
||||
"#define out varying\n"
|
||||
"#elif __VERSION__ < 130\n"
|
||||
"#define ROUND(x) (sign(x) * floor(abs(x) + .5))\n"
|
||||
"#define highp\n"
|
||||
"#define in attribute\n"
|
||||
"#define out varying\n"
|
||||
"#else\n"
|
||||
"#define ROUND(x) round(x)\n"
|
||||
"#endif\n";
|
||||
|
||||
static const char *const compatFragment =
|
||||
"#if defined(GL_ES)\n"
|
||||
"#define in varying\n"
|
||||
"#ifdef GL_FRAGMENT_PRECISION_HIGH\n"
|
||||
"precision highp float;\n"
|
||||
"#else\n"
|
||||
"precision mediump float;\n"
|
||||
"#endif\n"
|
||||
"#define OUTPUT\n"
|
||||
"#define outColor gl_FragColor\n"
|
||||
"#define texture texture2D\n"
|
||||
"#elif __VERSION__ < 130\n"
|
||||
"#define in varying\n"
|
||||
"#define OUTPUT\n"
|
||||
"#define outColor gl_FragColor\n"
|
||||
"#define texture texture2D\n"
|
||||
"#else\n"
|
||||
"#define OUTPUT out vec4 outColor;\n"
|
||||
"#endif\n";
|
||||
|
||||
// OGLES2 on AmigaOS doesn't support uniform booleans, let's introduce some shim
|
||||
#if defined(__amigaos4__)
|
||||
static const char *const compatUniformBool =
|
||||
"#define UBOOL mediump int\n"
|
||||
"#define UBOOL_TEST(v) (v != 0)\n";
|
||||
#else
|
||||
static const char *const compatUniformBool =
|
||||
"#define UBOOL bool\n"
|
||||
"#define UBOOL_TEST(v) v\n";
|
||||
#endif
|
||||
|
||||
|
||||
static const GLchar *readFile(const Common::String &filename) {
|
||||
Common::File file;
|
||||
Common::Path shaderDir;
|
||||
|
||||
#ifndef RELEASE_BUILD
|
||||
// Allow load shaders from source code directory without install them.
|
||||
// It's used for development purpose.
|
||||
// Additionally allow load shaders outside distribution data path,
|
||||
// 'extrapath' is used temporary in SearchMan.
|
||||
SearchMan.addDirectory("GRIM_SHADERS", "engines/grim", 0, 2);
|
||||
SearchMan.addDirectory("MYST3_SHADERS", "engines/myst3", 0, 2);
|
||||
SearchMan.addDirectory("STARK_SHADERS", "engines/stark", 0, 2);
|
||||
SearchMan.addDirectory("WINTERMUTE_SHADERS", "engines/wintermute/base/gfx/opengl", 0, 2);
|
||||
SearchMan.addDirectory("PLAYGROUND3D_SHADERS", "engines/playground3d", 0, 2);
|
||||
SearchMan.addDirectory("FREESCAPE_SHADERS", "engines/freescape", 0, 2);
|
||||
SearchMan.addDirectory("HPL1_SHADERS", "engines/hpl1/engine/impl", 0, 2);
|
||||
#endif
|
||||
|
||||
if (ConfMan.hasKey("extrapath")) {
|
||||
SearchMan.addDirectory("EXTRA_PATH", Common::FSNode(ConfMan.getPath("extrapath")), 0, 2);
|
||||
}
|
||||
#if !defined(IPHONE)
|
||||
shaderDir = Common::Path("shaders/", '/');
|
||||
#endif
|
||||
file.open(shaderDir.appendComponent(filename));
|
||||
if (!file.isOpen())
|
||||
error("Could not open shader %s!", filename.c_str());
|
||||
|
||||
#ifndef RELEASE_BUILD
|
||||
SearchMan.remove("GRIM_SHADERS");
|
||||
SearchMan.remove("MYST3_SHADERS");
|
||||
SearchMan.remove("STARK_SHADERS");
|
||||
SearchMan.remove("WINTERMUTE_SHADERS");
|
||||
SearchMan.remove("PLAYGROUND3D_SHADERS");
|
||||
SearchMan.remove("FREESCAPE_SHADERS");
|
||||
SearchMan.remove("HPL1_SHADERS");
|
||||
#endif
|
||||
|
||||
SearchMan.remove("EXTRA_PATH");
|
||||
|
||||
const int32 size = file.size();
|
||||
GLchar *shaderSource = new GLchar[size + 1];
|
||||
|
||||
file.read(shaderSource, size);
|
||||
file.close();
|
||||
shaderSource[size] = '\0';
|
||||
return shaderSource;
|
||||
}
|
||||
|
||||
GLuint Shader::createDirectShader(size_t shaderSourcesCount, const char *const *shaderSources, GLenum shaderType, const Common::String &name) {
|
||||
GLuint shader;
|
||||
GL_ASSIGN(shader, glCreateShader(shaderType));
|
||||
GL_CALL(glShaderSource(shader, shaderSourcesCount, shaderSources, NULL));
|
||||
GL_CALL(glCompileShader(shader));
|
||||
|
||||
GLint status;
|
||||
GL_CALL(glGetShaderiv(shader, GL_COMPILE_STATUS, &status));
|
||||
if (status != GL_TRUE) {
|
||||
GLint logSize;
|
||||
GL_CALL(glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logSize));
|
||||
GLchar *log = new GLchar[logSize];
|
||||
GL_CALL(glGetShaderInfoLog(shader, logSize, nullptr, log));
|
||||
|
||||
_error = Common::String::format("Could not compile shader %s: %s", name.c_str(), log);
|
||||
warning("Shader::createDirectShader(): %s", _error.c_str());
|
||||
|
||||
delete[] log;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
GLuint Shader::createCompatShader(const char *shaderSource, GLenum shaderType, const Common::String &name, int compatGLSLVersion) {
|
||||
GLchar versionSource[20];
|
||||
if (OpenGLContext.type == kContextGLES2) {
|
||||
switch(compatGLSLVersion) {
|
||||
case 110:
|
||||
case 120:
|
||||
// GLSL ES 1.00 is a subset of GLSL 1.20
|
||||
compatGLSLVersion = 100;
|
||||
break;
|
||||
default:
|
||||
_error = Common::String::format("Invalid GLSL version %d", compatGLSLVersion);
|
||||
warning("Shader: createCompatShader(): %s", _error.c_str());
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
switch(compatGLSLVersion) {
|
||||
case 110:
|
||||
case 120:
|
||||
break;
|
||||
default:
|
||||
_error = Common::String::format("Invalid GLSL version %d", compatGLSLVersion);
|
||||
warning("Shader: createCompatShader(): %s", _error.c_str());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (OpenGLContext.glslVersion < compatGLSLVersion) {
|
||||
_error = Common::String::format("Required GLSL version %d is not supported (%d maximum)", compatGLSLVersion, OpenGLContext.glslVersion);
|
||||
|
||||
warning("Shader: createCompatShader(): %s", _error.c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
Common::sprintf_s(versionSource, "#version %d\n", compatGLSLVersion);
|
||||
|
||||
const GLchar *compatSource =
|
||||
shaderType == GL_VERTEX_SHADER ? compatVertex : compatFragment;
|
||||
const GLchar *shaderSources[] = {
|
||||
versionSource,
|
||||
compatSource,
|
||||
compatUniformBool,
|
||||
shaderSource
|
||||
};
|
||||
|
||||
GLuint shader;
|
||||
GL_ASSIGN(shader, glCreateShader(shaderType));
|
||||
GL_CALL(glShaderSource(shader, 4, shaderSources, NULL));
|
||||
GL_CALL(glCompileShader(shader));
|
||||
|
||||
GLint status;
|
||||
GL_CALL(glGetShaderiv(shader, GL_COMPILE_STATUS, &status));
|
||||
if (status != GL_TRUE) {
|
||||
GLint logSize;
|
||||
GL_CALL(glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logSize));
|
||||
GLchar *log = new GLchar[logSize];
|
||||
GL_CALL(glGetShaderInfoLog(shader, logSize, nullptr, log));
|
||||
|
||||
_error = Common::String::format("Could not compile shader %s: %s", name.c_str(), log);
|
||||
warning("Shader: createCompatShader(): %s", _error.c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
GLuint Shader::loadShaderFromFile(const char *base, const char *extension, GLenum shaderType, int compatGLSLVersion) {
|
||||
const Common::String filename = Common::String(base) + "." + extension;
|
||||
const GLchar *shaderSource = readFile(filename);
|
||||
|
||||
GLuint shader;
|
||||
if (compatGLSLVersion) {
|
||||
shader = createCompatShader(shaderSource, shaderType, filename, compatGLSLVersion);
|
||||
} else {
|
||||
shader = createDirectShader(1, &shaderSource, shaderType, filename);
|
||||
}
|
||||
|
||||
delete[] shaderSource;
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* A deleter for OpenGL programs pointers which can be used with Common::SharedPtr.
|
||||
*/
|
||||
struct SharedPtrProgramDeleter {
|
||||
void operator()(GLuint *ptr) {
|
||||
if (ptr) {
|
||||
GL_CALL(glDeleteProgram(*ptr));
|
||||
}
|
||||
delete ptr;
|
||||
}
|
||||
};
|
||||
|
||||
Shader *Shader::_previousShader = nullptr;
|
||||
uint32 Shader::previousNumAttributes = 0;
|
||||
|
||||
Shader::Shader() {
|
||||
}
|
||||
|
||||
bool Shader::loadShader(const Common::String &name, GLuint vertexShader, GLuint fragmentShader, const char *const *attributes) {
|
||||
assert(attributes);
|
||||
|
||||
_name = name;
|
||||
|
||||
GLuint shaderProgram;
|
||||
GL_ASSIGN(shaderProgram, glCreateProgram());
|
||||
GL_CALL(glAttachShader(shaderProgram, vertexShader));
|
||||
GL_CALL(glAttachShader(shaderProgram, fragmentShader));
|
||||
|
||||
for (int idx = 0; attributes[idx]; ++idx) {
|
||||
GL_CALL(glBindAttribLocation(shaderProgram, idx, attributes[idx]));
|
||||
_attributes.push_back(VertexAttrib(idx, attributes[idx]));
|
||||
}
|
||||
GL_CALL(glLinkProgram(shaderProgram));
|
||||
|
||||
GLint status;
|
||||
GL_CALL(glGetProgramiv(shaderProgram, GL_LINK_STATUS, &status));
|
||||
if (status != GL_TRUE) {
|
||||
GLint logSize;
|
||||
GL_CALL(glGetProgramiv(shaderProgram, GL_INFO_LOG_LENGTH, &logSize));
|
||||
GLchar *log = new GLchar[logSize];
|
||||
GL_CALL(glGetProgramInfoLog(shaderProgram, logSize, nullptr, log));
|
||||
|
||||
_error = Common::String::format("Could not link shader %s: %s", name.c_str(), log);
|
||||
warning("Shader:Shader(): %s", _error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
GL_CALL(glDetachShader(shaderProgram, vertexShader));
|
||||
GL_CALL(glDetachShader(shaderProgram, fragmentShader));
|
||||
|
||||
GL_CALL(glDeleteShader(vertexShader));
|
||||
GL_CALL(glDeleteShader(fragmentShader));
|
||||
|
||||
_shaderNo = Common::SharedPtr<GLuint>(new GLuint(shaderProgram), SharedPtrProgramDeleter());
|
||||
_uniforms = Common::SharedPtr<UniformsMap>(new UniformsMap());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Shader *Shader::fromStrings(const Common::String &name, const char *vertex, const char *fragment, const char *const *attributes, int compatGLSLVersion) {
|
||||
Shader *shader = new Shader;
|
||||
|
||||
shader->loadFromStrings(name, vertex, fragment, attributes, compatGLSLVersion);
|
||||
|
||||
if (shader->hasError())
|
||||
error("%s", shader->getError().c_str());
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
bool Shader::loadFromStrings(const Common::String &name, const char *vertex, const char *fragment, const char *const *attributes, int compatGLSLVersion) {
|
||||
GLuint vertexShader, fragmentShader;
|
||||
|
||||
if (compatGLSLVersion) {
|
||||
vertexShader = createCompatShader(vertex, GL_VERTEX_SHADER, name + ".vertex", compatGLSLVersion);
|
||||
|
||||
if (!vertexShader)
|
||||
return false;
|
||||
|
||||
fragmentShader = createCompatShader(fragment, GL_FRAGMENT_SHADER, name + ".fragment", compatGLSLVersion);
|
||||
} else {
|
||||
vertexShader = createDirectShader(1, &vertex, GL_VERTEX_SHADER, name + ".vertex");
|
||||
|
||||
if (!vertexShader)
|
||||
return false;
|
||||
|
||||
fragmentShader = createDirectShader(1, &fragment, GL_FRAGMENT_SHADER, name + ".fragment");
|
||||
}
|
||||
|
||||
if (!fragmentShader)
|
||||
return false;
|
||||
|
||||
return loadShader(name, vertexShader, fragmentShader, attributes);
|
||||
}
|
||||
|
||||
bool Shader::loadFromStringsArray(const Common::String &name,
|
||||
size_t vertexCount, const char *const *vertex,
|
||||
size_t fragmentCount, const char *const *fragment,
|
||||
const char *const *attributes) {
|
||||
GLuint vertexShader, fragmentShader;
|
||||
|
||||
vertexShader = createDirectShader(vertexCount, vertex, GL_VERTEX_SHADER, name + ".vertex");
|
||||
|
||||
if (!vertexShader)
|
||||
return false;
|
||||
|
||||
fragmentShader = createDirectShader(fragmentCount, fragment, GL_FRAGMENT_SHADER, name + ".fragment");
|
||||
|
||||
if (!fragmentShader)
|
||||
return false;
|
||||
|
||||
return loadShader(name, vertexShader, fragmentShader, attributes);
|
||||
}
|
||||
|
||||
Shader *Shader::fromFiles(const char *vertex, const char *fragment, const char *const *attributes, int compatGLSLVersion) {
|
||||
Shader *shader = new Shader;
|
||||
|
||||
shader->loadFromFiles(vertex, fragment, attributes, compatGLSLVersion);
|
||||
|
||||
if (shader->hasError())
|
||||
error("%s", shader->getError().c_str());
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
bool Shader::loadFromFiles(const char *vertex, const char *fragment, const char *const *attributes, int compatGLSLVersion) {
|
||||
GLuint vertexShader = loadShaderFromFile(vertex, "vertex", GL_VERTEX_SHADER, compatGLSLVersion);
|
||||
|
||||
if (!vertexShader)
|
||||
return false;
|
||||
|
||||
GLuint fragmentShader = loadShaderFromFile(fragment, "fragment", GL_FRAGMENT_SHADER, compatGLSLVersion);
|
||||
|
||||
if (!fragmentShader)
|
||||
return false;
|
||||
|
||||
Common::String name = Common::String::format("%s/%s", vertex, fragment);
|
||||
|
||||
return loadShader(name, vertexShader, fragmentShader, attributes);
|
||||
}
|
||||
|
||||
void Shader::use(bool forceReload) {
|
||||
if (this == _previousShader && !forceReload)
|
||||
return;
|
||||
|
||||
// The previous shader might have had more attributes. Disable any extra ones.
|
||||
if (_attributes.size() < previousNumAttributes) {
|
||||
for (uint32 i = _attributes.size(); i < previousNumAttributes; ++i) {
|
||||
GL_CALL(glDisableVertexAttribArray(i));
|
||||
}
|
||||
}
|
||||
|
||||
_previousShader = this;
|
||||
previousNumAttributes = _attributes.size();
|
||||
|
||||
GL_CALL(glUseProgram(*_shaderNo));
|
||||
for (uint32 i = 0; i < _attributes.size(); ++i) {
|
||||
VertexAttrib &attrib = _attributes[i];
|
||||
if (attrib._enabled) {
|
||||
GL_CALL(glEnableVertexAttribArray(i));
|
||||
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, attrib._vbo));
|
||||
GL_CALL(glVertexAttribPointer(i, attrib._size, attrib._type, attrib._normalized, attrib._stride, (const void *)attrib._pointer));
|
||||
} else {
|
||||
GL_CALL(glDisableVertexAttribArray(i));
|
||||
switch (attrib._size) {
|
||||
case 2:
|
||||
GL_CALL(glVertexAttrib2fv(i, attrib._const));
|
||||
break;
|
||||
case 3:
|
||||
GL_CALL(glVertexAttrib3fv(i, attrib._const));
|
||||
break;
|
||||
case 4:
|
||||
GL_CALL(glVertexAttrib4fv(i, attrib._const));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||
}
|
||||
|
||||
GLuint Shader::createBuffer(GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage) {
|
||||
GLuint vbo;
|
||||
GL_CALL(glGenBuffers(1, &vbo));
|
||||
GL_CALL(glBindBuffer(target, vbo));
|
||||
GL_CALL(glBufferData(target, size, data, usage));
|
||||
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||
return vbo;
|
||||
}
|
||||
|
||||
void Shader::freeBuffer(GLuint vbo) {
|
||||
GL_CALL(glDeleteBuffers(1, &vbo));
|
||||
}
|
||||
|
||||
bool Shader::addAttribute(const char *attrib) {
|
||||
// Once we are linked we can't rebind the attribute so we have to deal with its place defined by OpenGL
|
||||
// As we store attribute at its OpenGL index, we will end up with empty attributes in the middle
|
||||
uint32 i;
|
||||
for (i = 0; i < _attributes.size(); ++i)
|
||||
if (_attributes[i]._name.equals(attrib))
|
||||
return true;
|
||||
|
||||
GLint result = -1;
|
||||
GL_ASSIGN(result, glGetAttribLocation(*_shaderNo, attrib));
|
||||
if (result < 0)
|
||||
return false;
|
||||
|
||||
|
||||
// Make sure we can store our new attribute
|
||||
if (_attributes.size() <= (uint)result) {
|
||||
for(; i < (uint)result; i++) {
|
||||
_attributes.push_back(VertexAttrib(i, ""));
|
||||
}
|
||||
_attributes.push_back(VertexAttrib(result, attrib));
|
||||
}
|
||||
|
||||
_attributes[result] = VertexAttrib(result, attrib);
|
||||
return true;
|
||||
}
|
||||
|
||||
VertexAttrib &Shader::getAttributeAt(uint32 idx) {
|
||||
assert(idx < _attributes.size());
|
||||
return _attributes[idx];
|
||||
}
|
||||
|
||||
VertexAttrib &Shader::getAttribute(const char *attrib) {
|
||||
for (uint32 i = 0; i < _attributes.size(); ++i)
|
||||
if (_attributes[i]._name.equals(attrib))
|
||||
return _attributes[i];
|
||||
|
||||
_error = Common::String::format("Could not find attribute %s in shader %s", attrib, _name.c_str());
|
||||
warning("Shader: getAttribute(): %s", _error.c_str());
|
||||
return _attributes[0];
|
||||
}
|
||||
|
||||
void Shader::enableVertexAttribute(const char *attrib, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer) {
|
||||
VertexAttrib &va = getAttribute(attrib);
|
||||
va._enabled = true;
|
||||
va._vbo = 0;
|
||||
va._size = size;
|
||||
va._type = type;
|
||||
va._normalized = normalized;
|
||||
va._stride = stride;
|
||||
va._pointer = (uintptr)pointer;
|
||||
}
|
||||
|
||||
void Shader::enableVertexAttribute(const char *attrib, GLuint vbo, GLint size, GLenum type, GLboolean normalized, GLsizei stride, uint32 offset) {
|
||||
VertexAttrib &va = getAttribute(attrib);
|
||||
va._enabled = true;
|
||||
va._vbo = vbo;
|
||||
va._size = size;
|
||||
va._type = type;
|
||||
va._normalized = normalized;
|
||||
va._stride = stride;
|
||||
va._pointer = offset;
|
||||
}
|
||||
|
||||
void Shader::disableVertexAttribute(const char *attrib, int size, const float *data) {
|
||||
VertexAttrib &va = getAttribute(attrib);
|
||||
va._enabled = false;
|
||||
va._size = size;
|
||||
for (int i = 0; i < size; ++i)
|
||||
va._const[i] = data[i];
|
||||
}
|
||||
|
||||
void Shader::unbind() {
|
||||
GL_CALL(glUseProgram(0));
|
||||
_previousShader = nullptr;
|
||||
|
||||
// Disable all vertex attributes as well
|
||||
for (uint32 i = 0; i < previousNumAttributes; ++i) {
|
||||
GL_CALL(glDisableVertexAttribArray(i));
|
||||
}
|
||||
previousNumAttributes = 0;
|
||||
}
|
||||
|
||||
Shader::~Shader() {
|
||||
// If this is the currently active shader, unbind
|
||||
if (_previousShader == this) {
|
||||
unbind();
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace OpenGL
|
||||
|
||||
#endif
|
||||
270
graphics/opengl/shader.h
Normal file
270
graphics/opengl/shader.h
Normal file
@@ -0,0 +1,270 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRAPHICS_OPENGL_SHADER_H
|
||||
#define GRAPHICS_OPENGL_SHADER_H
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/array.h"
|
||||
#include "common/ptr.h"
|
||||
|
||||
#include "math/matrix3.h"
|
||||
#include "math/matrix4.h"
|
||||
#include "math/vector2d.h"
|
||||
#include "math/vector3d.h"
|
||||
#include "math/vector4d.h"
|
||||
|
||||
#include "graphics/opengl/debug.h"
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
struct VertexAttrib {
|
||||
VertexAttrib(uint32 idx, const char *name) :
|
||||
_enabled(false), _idx(idx), _name(name), _vbo(0), _size(0),
|
||||
_type(GL_FLOAT), _normalized(false), _stride(0), _pointer(0) {}
|
||||
bool _enabled;
|
||||
uint32 _idx;
|
||||
Common::String _name;
|
||||
GLuint _vbo;
|
||||
GLint _size;
|
||||
GLenum _type;
|
||||
bool _normalized;
|
||||
GLsizei _stride;
|
||||
uintptr _pointer;
|
||||
float _const[4];
|
||||
};
|
||||
|
||||
class Shader {
|
||||
typedef Common::HashMap<Common::String, GLint> UniformsMap;
|
||||
|
||||
public:
|
||||
Shader();
|
||||
~Shader();
|
||||
Shader *clone() {
|
||||
return new Shader(*this);
|
||||
}
|
||||
|
||||
void use(bool forceReload = false);
|
||||
|
||||
bool setUniform(const Common::String &uniform, const Math::Matrix4 &m) {
|
||||
GLint pos = getUniformLocation(uniform);
|
||||
if (pos != -1) {
|
||||
use();
|
||||
GL_CALL(glUniformMatrix4fv(pos, 1, GL_FALSE, m.getData()));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool setUniform(const Common::String &uniform, const Math::Matrix3 &m) {
|
||||
GLint pos = getUniformLocation(uniform);
|
||||
if (pos != -1) {
|
||||
use();
|
||||
GL_CALL(glUniformMatrix3fv(pos, 1, GL_FALSE, m.getData()));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool setUniform(const Common::String &uniform, const Math::Vector4d &v) {
|
||||
GLint pos = getUniformLocation(uniform);
|
||||
if (pos != -1) {
|
||||
use();
|
||||
GL_CALL(glUniform4fv(pos, 1, v.getData()));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool setUniform(const Common::String &uniform, const Math::Vector3d &v) {
|
||||
GLint pos = getUniformLocation(uniform);
|
||||
if (pos != -1) {
|
||||
use();
|
||||
GL_CALL(glUniform3fv(pos, 1, v.getData()));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool setUniform(const Common::String &uniform, const Math::Vector2d &v) {
|
||||
GLint pos = getUniformLocation(uniform);
|
||||
if (pos != -1) {
|
||||
use();
|
||||
GL_CALL(glUniform2fv(pos, 1, v.getData()));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool setUniform(const Common::String &uniform, unsigned int x) {
|
||||
GLint pos = getUniformLocation(uniform);
|
||||
if (pos != -1) {
|
||||
use();
|
||||
GL_CALL(glUniform1i(pos, x));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool setUniform(const Common::String &uniform, const int size, const int *array) {
|
||||
GLint pos = getUniformLocation(uniform);
|
||||
if (pos != -1) {
|
||||
use();
|
||||
GL_CALL(glUniform1iv(pos, size, array));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Different name to avoid overload ambiguity
|
||||
bool setUniform1f(const Common::String &uniform, float f) {
|
||||
GLint pos = getUniformLocation(uniform);
|
||||
if (pos != -1) {
|
||||
use();
|
||||
GL_CALL(glUniform1f(pos, f));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
GLint getUniformLocation(const Common::String &uniform) const {
|
||||
UniformsMap::iterator kv = _uniforms->find(uniform);
|
||||
if (kv == _uniforms->end()) {
|
||||
GLint ret;
|
||||
GL_ASSIGN(ret, glGetUniformLocation(*_shaderNo, uniform.c_str()));
|
||||
_uniforms->setVal(uniform, ret);
|
||||
return ret;
|
||||
} else {
|
||||
return kv->_value;
|
||||
}
|
||||
}
|
||||
|
||||
void enableVertexAttribute(const char *attrib, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
|
||||
void enableVertexAttribute(const char *attrib, GLuint vbo, GLint size, GLenum type, GLboolean normalized, GLsizei stride, uint32 offset);
|
||||
void disableVertexAttribute(const char *attrib, int size, const float *data);
|
||||
template <int r>
|
||||
void disableVertexAttribute(const char *attrib, const Math::Matrix<r,1> &m) {
|
||||
disableVertexAttribute(attrib, r, m.getData());
|
||||
}
|
||||
bool addAttribute(const char *attrib);
|
||||
VertexAttrib & getAttributeAt(uint32 idx);
|
||||
VertexAttrib & getAttribute(const char *attrib);
|
||||
|
||||
static GLuint createBuffer(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage = GL_STATIC_DRAW);
|
||||
static void freeBuffer(GLuint vbo);
|
||||
|
||||
/**
|
||||
* Creates a shader object from files
|
||||
*
|
||||
* For shader files (used by games), we used to require GLSL 1.20, this is the default for compatGLSLVersion.
|
||||
* The GLSL version is converted to GLSL ES version if needed.
|
||||
*
|
||||
* @param name The name of the shader for errors messages
|
||||
* @param vertex The vertex shader code
|
||||
* @param fragment The fragment shader code
|
||||
* @param attributes The vertex attributes names for indexing
|
||||
* @param compatGLSLVersion The GLSL version required: 0 for no preprocessing, 100 for GLSL 1.00 and so on
|
||||
*
|
||||
* @return the shader object created
|
||||
*/
|
||||
static Shader *fromFiles(const char *vertex, const char *fragment, const char *const *attributes, int compatGLSLVersion = 120);
|
||||
static Shader *fromFiles(const char *shared, const char *const *attributes, int compatGLSLVersion = 120) {
|
||||
return fromFiles(shared, shared, attributes, compatGLSLVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a shader object from strings
|
||||
*
|
||||
* Shader strings are usually included in backends and don't need preprocessing, this is the default for compatGLSLVersion.
|
||||
* The GLSL version is converted to GLSL ES version if needed.
|
||||
*
|
||||
* @param name The name of the shader for errors messages
|
||||
* @param vertex The vertex shader code
|
||||
* @param fragment The fragment shader code
|
||||
* @param attributes The vertex attributes names for indexing
|
||||
* @param compatGLSLVersion The GLSL version required: 0 for no preprocessing, 100 for GLSL 1.00 and so on
|
||||
*
|
||||
* @return the shader object created
|
||||
*/
|
||||
static Shader *fromStrings(const Common::String &name, const char *vertex, const char *fragment, const char *const *attributes, int compatGLSLVersion = 0);
|
||||
|
||||
bool loadFromFiles(const char *vertex, const char *fragment, const char *const *attributes, int compatGLSLVersion = 120);
|
||||
bool loadFromStrings(const Common::String &name, const char *vertex, const char *fragment, const char *const *attributes, int compatGLSLVersion = 0);
|
||||
|
||||
/**
|
||||
* Creates a shader object from strings arrays
|
||||
*
|
||||
* Everything is loaded directly without any preprocessing.
|
||||
*
|
||||
* @param name The name of the shader for errors messages
|
||||
* @param vertexCount The number of vertex shader code parts
|
||||
* @param vertex The vertex shader code parts
|
||||
* @param fragmentCount The number of fragment shader code parts
|
||||
* @param fragment The fragment shader code parts
|
||||
* @param attributes The vertex attributes names for indexing
|
||||
*
|
||||
* @return the loading status
|
||||
*/
|
||||
bool loadFromStringsArray(const Common::String &name,
|
||||
size_t vertexCount, const char *const *vertex,
|
||||
size_t fragmentCount, const char *const *fragment,
|
||||
const char *const *attributes);
|
||||
|
||||
void unbind();
|
||||
|
||||
Common::String &getError() { return _error; }
|
||||
bool hasError() { return !_error.empty(); }
|
||||
|
||||
private:
|
||||
bool loadShader(const Common::String &name, GLuint vertexShader, GLuint fragmentShader, const char *const *attributes);
|
||||
|
||||
GLuint createCompatShader(const char *shaderSource, GLenum shaderType, const Common::String &name, int compatGLSLVersion);
|
||||
GLuint createDirectShader(size_t shaderSourcesCount, const char *const *shaderSources, GLenum shaderType, const Common::String &name);
|
||||
GLuint loadShaderFromFile(const char *base, const char *extension, GLenum shaderType, int compatGLSLVersion);
|
||||
|
||||
// Since this class is cloned using the implicit copy constructor,
|
||||
// a reference counting pointer is used to ensure deletion of the OpenGL
|
||||
// program upon destruction of the last clone.
|
||||
Common::SharedPtr<GLuint> _shaderNo;
|
||||
|
||||
Common::String _name;
|
||||
|
||||
Common::Array<VertexAttrib> _attributes;
|
||||
Common::SharedPtr<UniformsMap> _uniforms;
|
||||
|
||||
static Shader *_previousShader;
|
||||
static uint32 previousNumAttributes;
|
||||
|
||||
Common::String _error;
|
||||
};
|
||||
|
||||
} // End of namespace OpenGL
|
||||
|
||||
#endif
|
||||
153
graphics/opengl/system_headers.h
Normal file
153
graphics/opengl/system_headers.h
Normal file
@@ -0,0 +1,153 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRAPHICS_OPENGL_SYSTEM_HEADERS_H
|
||||
#define GRAPHICS_OPENGL_SYSTEM_HEADERS_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
// On macOS we only support GL contexts. The reason is that Apple's GL interface
|
||||
// uses "void *" for GLhandleARB which is not type compatible with GLint. This
|
||||
// kills our aliasing trick for extension functions and thus would force us to
|
||||
// supply two different Shader class implementations or introduce other
|
||||
// wrappers. macOS only supports GL contexts right now anyway (at least
|
||||
// according to SDL2 sources), thus it is not much of an issue.
|
||||
#if defined(MACOSX) && (!defined(USE_GLES_MODE) || USE_GLES_MODE != 0)
|
||||
//#warning "Only forced OpenGL mode is supported on macOS. Overriding settings."
|
||||
#undef USE_GLES_MODE
|
||||
#define USE_GLES_MODE 0
|
||||
#endif
|
||||
|
||||
// We allow to force GL or GLES modes on compile time.
|
||||
// For this the USE_GLES_MODE define is used. The following values represent
|
||||
// the given selection choices:
|
||||
// 0 - Force OpenGL context
|
||||
// 1 - Force OpenGL ES context
|
||||
// 2 - Force OpenGL ES 2.0 context
|
||||
#ifdef USE_GLES_MODE
|
||||
#define USE_FORCED_GL (USE_GLES_MODE == 0)
|
||||
#define USE_FORCED_GLES (USE_GLES_MODE == 1)
|
||||
#define USE_FORCED_GLES2 (USE_GLES_MODE == 2)
|
||||
#else
|
||||
#define USE_FORCED_GL 0
|
||||
#define USE_FORCED_GLES 0
|
||||
#define USE_FORCED_GLES2 0
|
||||
#endif
|
||||
|
||||
// Don't include any OpenGL stuff if we didn't enable it
|
||||
#ifdef USE_OPENGL
|
||||
#ifdef USE_GLAD
|
||||
|
||||
#include "graphics/opengl/glad.h"
|
||||
|
||||
#elif USE_FORCED_GLES2
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#if defined(IPHONE)
|
||||
#include <OpenGLES/ES2/gl.h>
|
||||
#include <OpenGLES/ES2/glext.h>
|
||||
#else
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#endif
|
||||
#undef GL_GLEXT_PROTOTYPES
|
||||
|
||||
#elif USE_FORCED_GLES
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#if defined(IPHONE)
|
||||
#include <OpenGLES/ES1/gl.h>
|
||||
#include <OpenGLES/ES1/glext.h>
|
||||
#else
|
||||
#include <GLES/gl.h>
|
||||
#include <GLES/glext.h>
|
||||
#endif
|
||||
#undef GL_GLEXT_PROTOTYPES
|
||||
|
||||
#else
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#undef GL_GLEXT_PROTOTYPES
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(GL_TEXTURE_MAX_LEVEL) && defined(GL_TEXTURE_MAX_LEVEL_APPLE)
|
||||
#define GL_TEXTURE_MAX_LEVEL GL_TEXTURE_MAX_LEVEL_APPLE
|
||||
#endif
|
||||
|
||||
#if !defined(GL_BGRA) && defined(GL_BGRA_EXT)
|
||||
#define GL_BGRA GL_BGRA_EXT
|
||||
#endif
|
||||
|
||||
#if !defined(GL_UNPACK_ROW_LENGTH)
|
||||
// The Android SDK does not declare GL_UNPACK_ROW_LENGTH_EXT
|
||||
#define GL_UNPACK_ROW_LENGTH 0x0CF2
|
||||
#endif
|
||||
|
||||
#if !defined(GL_MAX_SAMPLES)
|
||||
// The Android SDK and SDL1 don't declare GL_MAX_SAMPLES
|
||||
#define GL_MAX_SAMPLES 0x8D57
|
||||
#endif
|
||||
|
||||
#if !defined(GL_STACK_OVERFLOW)
|
||||
#define GL_STACK_OVERFLOW 0x0503
|
||||
#endif
|
||||
|
||||
#if !defined(GL_STACK_UNDERFLOW)
|
||||
#define GL_STACK_UNDERFLOW 0x0504
|
||||
#endif
|
||||
|
||||
#if !defined(GL_CLAMP_TO_BORDER)
|
||||
#define GL_CLAMP_TO_BORDER 0x812D
|
||||
#endif
|
||||
|
||||
#if !defined(GL_MIRRORED_REPEAT)
|
||||
#define GL_MIRRORED_REPEAT 0x8370
|
||||
#endif
|
||||
|
||||
#if !defined(GL_DRAW_FRAMEBUFFER_BINDING)
|
||||
#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6
|
||||
#endif
|
||||
|
||||
#if !defined(GL_DEPTH_COMPONENT24)
|
||||
// For GLES2 with GL_OES_depth24
|
||||
#define GL_DEPTH_COMPONENT24 0x81A6
|
||||
#endif
|
||||
|
||||
#if !defined(GL_DEPTH_STENCIL)
|
||||
// For GLES2 with GL_OES_packed_depth_stencil
|
||||
#define GL_DEPTH_STENCIL 0x84F9
|
||||
#endif
|
||||
|
||||
#if !defined(GL_DEPTH24_STENCIL8)
|
||||
// For GLES2 with GL_OES_packed_depth_stencil
|
||||
#define GL_DEPTH24_STENCIL8 0x88F0
|
||||
#endif
|
||||
|
||||
#if !defined(GL_DEPTH_STENCIL_ATTACHMENT)
|
||||
// For WebGL: see https://github.com/emscripten-core/emscripten/issues/4832
|
||||
#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A
|
||||
#endif
|
||||
|
||||
#endif
|
||||
281
graphics/opengl/texture.cpp
Normal file
281
graphics/opengl/texture.cpp
Normal file
@@ -0,0 +1,281 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
|
||||
#ifdef USE_OPENGL
|
||||
|
||||
#include "graphics/opengl/texture.h"
|
||||
#include "graphics/opengl/debug.h"
|
||||
|
||||
#include "common/algorithm.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
Texture::Texture(GLenum glIntFormat, GLenum glFormat, GLenum glType, bool autoCreate)
|
||||
: _glIntFormat(glIntFormat), _glFormat(glFormat), _glType(glType),
|
||||
_width(0), _height(0), _logicalWidth(0), _logicalHeight(0),
|
||||
_flip(false), _rotation(Common::kRotationNormal),
|
||||
_texCoords(), _glFilter(GL_NEAREST), _glTexture(0) {
|
||||
if (autoCreate)
|
||||
create();
|
||||
}
|
||||
|
||||
Texture::~Texture() {
|
||||
GL_CALL_SAFE(glDeleteTextures, (1, &_glTexture));
|
||||
}
|
||||
|
||||
void Texture::enableLinearFiltering(bool enable) {
|
||||
if (enable) {
|
||||
_glFilter = GL_LINEAR;
|
||||
} else {
|
||||
_glFilter = GL_NEAREST;
|
||||
}
|
||||
|
||||
if (!bind()) {
|
||||
return;
|
||||
}
|
||||
|
||||
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _glFilter));
|
||||
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _glFilter));
|
||||
}
|
||||
|
||||
void Texture::setWrapMode(WrapMode wrapMode) {
|
||||
GLuint glwrapMode;
|
||||
|
||||
switch(wrapMode) {
|
||||
case kWrapModeBorder:
|
||||
if (OpenGLContext.textureBorderClampSupported) {
|
||||
glwrapMode = GL_CLAMP_TO_BORDER;
|
||||
break;
|
||||
}
|
||||
// fall through
|
||||
case kWrapModeEdge:
|
||||
if (OpenGLContext.textureEdgeClampSupported) {
|
||||
glwrapMode = GL_CLAMP_TO_EDGE;
|
||||
break;
|
||||
} else {
|
||||
#if !USE_FORCED_GLES && !USE_FORCED_GLES2
|
||||
// Fallback on clamp
|
||||
glwrapMode = GL_CLAMP;
|
||||
#else
|
||||
// This fallback should never happen in real life (GLES/GLES2 have border/edge clamp)
|
||||
glwrapMode = GL_REPEAT;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case kWrapModeMirroredRepeat:
|
||||
if (OpenGLContext.textureMirrorRepeatSupported) {
|
||||
glwrapMode = GL_MIRRORED_REPEAT;
|
||||
break;
|
||||
}
|
||||
// fall through
|
||||
case kWrapModeRepeat:
|
||||
default:
|
||||
glwrapMode = GL_REPEAT;
|
||||
}
|
||||
|
||||
|
||||
if (!bind()) {
|
||||
return;
|
||||
}
|
||||
|
||||
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glwrapMode));
|
||||
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glwrapMode));
|
||||
}
|
||||
|
||||
void Texture::destroy() {
|
||||
if (!_glTexture) {
|
||||
return;
|
||||
}
|
||||
GL_CALL(glDeleteTextures(1, &_glTexture));
|
||||
_glTexture = 0;
|
||||
}
|
||||
|
||||
void Texture::create() {
|
||||
// Release old texture name in case it exists.
|
||||
destroy();
|
||||
|
||||
// Get a new texture name.
|
||||
GL_CALL(glGenTextures(1, &_glTexture));
|
||||
|
||||
// Set up all texture parameters.
|
||||
bind();
|
||||
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _glFilter));
|
||||
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _glFilter));
|
||||
if (OpenGLContext.textureEdgeClampSupported) {
|
||||
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
|
||||
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
|
||||
} else {
|
||||
#if !USE_FORCED_GLES && !USE_FORCED_GLES2
|
||||
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP));
|
||||
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP));
|
||||
#endif
|
||||
}
|
||||
|
||||
// If a size is specified, allocate memory for it.
|
||||
if (_width != 0 && _height != 0) {
|
||||
// Allocate storage for OpenGL texture.
|
||||
GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, _glIntFormat, _width, _height,
|
||||
0, _glFormat, _glType, nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
bool Texture::bind() const {
|
||||
if (!_glTexture) {
|
||||
return false;
|
||||
}
|
||||
GL_CALL(glBindTexture(GL_TEXTURE_2D, _glTexture));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Texture::setSize(uint width, uint height) {
|
||||
const uint oldWidth = _width;
|
||||
const uint oldHeight = _height;
|
||||
|
||||
_logicalWidth = width;
|
||||
_logicalHeight = height;
|
||||
|
||||
if (!OpenGLContext.NPOTSupported) {
|
||||
_width = Common::nextHigher2(width);
|
||||
_height = Common::nextHigher2(height);
|
||||
} else {
|
||||
_width = width;
|
||||
_height = height;
|
||||
}
|
||||
|
||||
// If a size is specified, allocate memory for it.
|
||||
if (width != 0 && height != 0) {
|
||||
computeTexCoords();
|
||||
|
||||
// Allocate storage for OpenGL texture if necessary.
|
||||
if (oldWidth != _width || oldHeight != _height) {
|
||||
if (!bind()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool error;
|
||||
GL_CALL_CHECK(error, glTexImage2D(GL_TEXTURE_2D, 0, _glIntFormat, _width, _height,
|
||||
0, _glFormat, _glType, nullptr));
|
||||
if (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Texture::computeTexCoords() {
|
||||
const GLfloat texWidth = (GLfloat)_logicalWidth / _width;
|
||||
const GLfloat texHeight = (GLfloat)_logicalHeight / _height;
|
||||
|
||||
if (_flip) {
|
||||
_texCoords[0] = 0;
|
||||
_texCoords[1] = texHeight;
|
||||
|
||||
_texCoords[2] = texWidth;
|
||||
_texCoords[3] = texHeight;
|
||||
|
||||
_texCoords[4] = 0;
|
||||
_texCoords[5] = 0;
|
||||
|
||||
_texCoords[6] = texWidth;
|
||||
_texCoords[7] = 0;
|
||||
} else {
|
||||
_texCoords[0] = 0;
|
||||
_texCoords[1] = 0;
|
||||
|
||||
_texCoords[2] = texWidth;
|
||||
_texCoords[3] = 0;
|
||||
|
||||
_texCoords[4] = 0;
|
||||
_texCoords[5] = texHeight;
|
||||
|
||||
_texCoords[6] = texWidth;
|
||||
_texCoords[7] = texHeight;
|
||||
}
|
||||
|
||||
switch(_rotation) {
|
||||
default:
|
||||
case Common::kRotationNormal:
|
||||
// Nothing to do
|
||||
break;
|
||||
case Common::kRotation90:
|
||||
// LT -> LB and RB -> RT
|
||||
SWAP(_texCoords[1], _texCoords[7]);
|
||||
// RT -> LT and LB -> RB
|
||||
SWAP(_texCoords[2], _texCoords[4]);
|
||||
break;
|
||||
case Common::kRotation180:
|
||||
// LT -> RT and RT -> LT
|
||||
SWAP(_texCoords[0], _texCoords[2]);
|
||||
// RT -> RB and LB -> LT
|
||||
SWAP(_texCoords[1], _texCoords[5]);
|
||||
// LT -> LB and RB -> RT
|
||||
SWAP(_texCoords[3], _texCoords[7]);
|
||||
// LT -> RT and RT -> LT
|
||||
SWAP(_texCoords[4], _texCoords[6]);
|
||||
break;
|
||||
case Common::kRotation270:
|
||||
// LT -> RT and RB -> LB
|
||||
SWAP(_texCoords[0], _texCoords[6]);
|
||||
// RT -> RB and LB -> LT
|
||||
SWAP(_texCoords[3], _texCoords[5]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Texture::updateArea(const Common::Rect &area, const Graphics::Surface &src) {
|
||||
// Set the texture on the active texture unit.
|
||||
if (!bind()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the actual texture.
|
||||
// Although we have the area of the texture buffer we want to update we
|
||||
// cannot take advantage of the left/right boundaries here because it is
|
||||
// not possible to specify a pitch to glTexSubImage2D. To be precise, with
|
||||
// plain OpenGL we could set GL_UNPACK_ROW_LENGTH to achieve this. However,
|
||||
// OpenGL ES 1.0 does not support GL_UNPACK_ROW_LENGTH. Thus, we are left
|
||||
// with the following options:
|
||||
//
|
||||
// 1) (As we do right now) Simply always update the whole texture lines of
|
||||
// rect changed. This is simplest to implement. In case performance is
|
||||
// really an issue we can think of switching to another method.
|
||||
//
|
||||
// 2) Copy the dirty rect to a temporary buffer and upload that by using
|
||||
// glTexSubImage2D. This is what the Android backend does. It is more
|
||||
// complicated though.
|
||||
//
|
||||
// 3) Use glTexSubImage2D per line changed. This is what the old OpenGL
|
||||
// graphics manager did but it is much slower! Thus, we do not use it.
|
||||
GL_CALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||
GL_CALL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, area.top, src.w, area.height(),
|
||||
_glFormat, _glType, src.getBasePtr(0, area.top)));
|
||||
}
|
||||
|
||||
} // End of namespace OpenGL
|
||||
|
||||
#endif
|
||||
|
||||
198
graphics/opengl/texture.h
Normal file
198
graphics/opengl/texture.h
Normal file
@@ -0,0 +1,198 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRAPHICS_OPENGL_TEXTURE_H
|
||||
#define GRAPHICS_OPENGL_TEXTURE_H
|
||||
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
#include "graphics/opengl/context.h"
|
||||
|
||||
#include "graphics/pixelformat.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
#include "common/rect.h"
|
||||
#include "common/rotationmode.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
enum WrapMode {
|
||||
kWrapModeBorder,
|
||||
kWrapModeEdge,
|
||||
kWrapModeRepeat,
|
||||
kWrapModeMirroredRepeat
|
||||
};
|
||||
|
||||
/**
|
||||
* A simple GL texture object abstraction.
|
||||
*
|
||||
* This is used for low-level GL texture handling.
|
||||
*/
|
||||
class Texture {
|
||||
public:
|
||||
/**
|
||||
* Constrcut a new GL texture object.
|
||||
*
|
||||
* @param glIntFormat The internal format to use.
|
||||
* @param glFormat The input format.
|
||||
* @param glType The input type.
|
||||
*/
|
||||
Texture(GLenum glIntFormat, GLenum glFormat, GLenum glType, bool autoCreate = true);
|
||||
~Texture();
|
||||
|
||||
/**
|
||||
* Enable or disable linear texture filtering.
|
||||
*
|
||||
* @param enable true to enable and false to disable.
|
||||
*/
|
||||
void enableLinearFiltering(bool enable);
|
||||
|
||||
/**
|
||||
* Test whether linear filtering is enabled.
|
||||
*/
|
||||
bool isLinearFilteringEnabled() const { return (_glFilter == GL_LINEAR); }
|
||||
|
||||
/**
|
||||
* Enable or disable linear texture filtering.
|
||||
*
|
||||
* @param enable true to enable and false to disable.
|
||||
*/
|
||||
void setWrapMode(WrapMode wrapMode);
|
||||
|
||||
/**
|
||||
* Destroy the OpenGL texture name.
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* Create the OpenGL texture name.
|
||||
*/
|
||||
void create();
|
||||
|
||||
/**
|
||||
* Bind the texture to the active texture unit.
|
||||
*
|
||||
* @return Whether a texture really exists
|
||||
*/
|
||||
bool bind() const;
|
||||
|
||||
/**
|
||||
* Sets the size of the texture in pixels.
|
||||
*
|
||||
* The internal OpenGL texture might have a different size. To query the
|
||||
* actual size use getWidth()/getHeight().
|
||||
*
|
||||
* @param width The desired logical width.
|
||||
* @param height The desired logical height.
|
||||
* @return Whether the call was successful
|
||||
*/
|
||||
bool setSize(uint width, uint height);
|
||||
|
||||
/**
|
||||
* Sets the flip and rotate parameters of the texture
|
||||
*
|
||||
* @param flip Whether to flip vertically the texture when displaying it.
|
||||
*/
|
||||
void setFlip(bool flip) { if (_flip != flip) { _flip = flip; computeTexCoords(); } }
|
||||
|
||||
/**
|
||||
* Sets the rotate parameter of the texture
|
||||
*
|
||||
* @param rotation How to rotate the texture
|
||||
*/
|
||||
void setRotation(Common::RotationMode rotation) { if (_rotation != rotation) { _rotation = rotation; computeTexCoords(); } }
|
||||
|
||||
/**
|
||||
* Copy image data to the texture.
|
||||
*
|
||||
* @param area The area to update.
|
||||
* @param src Surface for the whole texture containing the pixel data
|
||||
* to upload. Only the area described by area will be
|
||||
* uploaded.
|
||||
*/
|
||||
void updateArea(const Common::Rect &area, const Graphics::Surface &src);
|
||||
|
||||
/**
|
||||
* Query the GL texture's width.
|
||||
*/
|
||||
uint getWidth() const { return _width; }
|
||||
|
||||
/**
|
||||
* Query the GL texture's height.
|
||||
*/
|
||||
uint getHeight() const { return _height; }
|
||||
|
||||
/**
|
||||
* Query the logical texture's width.
|
||||
*/
|
||||
uint getLogicalWidth() const { return _logicalWidth; }
|
||||
|
||||
/**
|
||||
* Query the logical texture's height.
|
||||
*/
|
||||
uint getLogicalHeight() const { return _logicalHeight; }
|
||||
|
||||
/**
|
||||
* Obtain texture coordinates for rectangular drawing.
|
||||
*/
|
||||
const GLfloat *getTexCoords() const { return _texCoords; }
|
||||
|
||||
/**
|
||||
* Obtain texture name.
|
||||
*
|
||||
* Beware that the texture name changes whenever create is used.
|
||||
* destroy will invalidate the texture name.
|
||||
*/
|
||||
GLuint getGLTexture() const { return _glTexture; }
|
||||
|
||||
static inline const Graphics::PixelFormat getRGBPixelFormat() {
|
||||
return Graphics::PixelFormat::createFormatRGB24();
|
||||
}
|
||||
|
||||
static inline const Graphics::PixelFormat getRGBAPixelFormat() {
|
||||
return Graphics::PixelFormat::createFormatRGBA32();
|
||||
}
|
||||
|
||||
static inline const Graphics::PixelFormat getBGRAPixelFormat() {
|
||||
return Graphics::PixelFormat::createFormatBGRA32();
|
||||
}
|
||||
|
||||
protected:
|
||||
void computeTexCoords();
|
||||
|
||||
const GLenum _glIntFormat;
|
||||
const GLenum _glFormat;
|
||||
const GLenum _glType;
|
||||
|
||||
uint _width, _height;
|
||||
uint _logicalWidth, _logicalHeight;
|
||||
bool _flip;
|
||||
Common::RotationMode _rotation;
|
||||
|
||||
GLfloat _texCoords[4*2];
|
||||
|
||||
GLint _glFilter;
|
||||
|
||||
GLuint _glTexture;
|
||||
};
|
||||
|
||||
} // End of namespace OpenGL
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user