2014-08-17 12:16:57 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <vector>
|
|
|
|
#include <string>
|
2017-02-19 11:22:23 +01:00
|
|
|
#include <algorithm>
|
2014-08-17 12:16:57 +02:00
|
|
|
#include <map>
|
|
|
|
|
|
|
|
#include "base/logging.h"
|
2015-10-10 16:41:19 +02:00
|
|
|
#include "math/dataconv.h"
|
2014-08-17 12:16:57 +02:00
|
|
|
#include "math/lin/matrix4x4.h"
|
|
|
|
#include "thin3d/thin3d.h"
|
2015-09-06 12:21:15 +02:00
|
|
|
#include "gfx/gl_common.h"
|
2017-03-03 14:15:27 +01:00
|
|
|
#include "gfx/gl_debug_log.h"
|
2017-02-04 18:46:12 +01:00
|
|
|
#include "gfx/GLStateCache.h"
|
2015-09-06 12:21:15 +02:00
|
|
|
#include "gfx_es2/gpu_features.h"
|
2014-09-06 13:49:01 +02:00
|
|
|
#include "gfx/gl_lost_manager.h"
|
2014-08-17 12:16:57 +02:00
|
|
|
|
2017-02-06 11:55:54 +01:00
|
|
|
#ifdef IOS
|
|
|
|
extern void bindDefaultFBO();
|
|
|
|
#endif
|
|
|
|
|
2016-12-25 18:18:19 +01:00
|
|
|
namespace Draw {
|
|
|
|
|
2014-08-17 12:16:57 +02:00
|
|
|
static const unsigned short compToGL[] = {
|
|
|
|
GL_NEVER,
|
|
|
|
GL_LESS,
|
|
|
|
GL_EQUAL,
|
|
|
|
GL_LEQUAL,
|
|
|
|
GL_GREATER,
|
|
|
|
GL_NOTEQUAL,
|
|
|
|
GL_GEQUAL,
|
|
|
|
GL_ALWAYS
|
|
|
|
};
|
|
|
|
|
|
|
|
static const unsigned short blendEqToGL[] = {
|
|
|
|
GL_FUNC_ADD,
|
|
|
|
GL_FUNC_SUBTRACT,
|
|
|
|
GL_FUNC_REVERSE_SUBTRACT,
|
2016-12-26 23:51:17 +01:00
|
|
|
GL_MIN,
|
|
|
|
GL_MAX,
|
2014-08-17 12:16:57 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static const unsigned short blendFactorToGL[] = {
|
|
|
|
GL_ZERO,
|
|
|
|
GL_ONE,
|
|
|
|
GL_SRC_COLOR,
|
|
|
|
GL_ONE_MINUS_SRC_COLOR,
|
|
|
|
GL_DST_COLOR,
|
|
|
|
GL_ONE_MINUS_DST_COLOR,
|
2016-12-26 23:51:17 +01:00
|
|
|
GL_SRC_ALPHA,
|
|
|
|
GL_ONE_MINUS_SRC_ALPHA,
|
|
|
|
GL_DST_ALPHA,
|
2014-08-17 12:16:57 +02:00
|
|
|
GL_ONE_MINUS_DST_ALPHA,
|
|
|
|
GL_CONSTANT_COLOR,
|
2016-12-26 23:51:17 +01:00
|
|
|
GL_ONE_MINUS_CONSTANT_COLOR,
|
|
|
|
GL_CONSTANT_ALPHA,
|
|
|
|
GL_ONE_MINUS_CONSTANT_ALPHA,
|
2016-12-26 23:59:42 +01:00
|
|
|
#if !defined(USING_GLES2) // TODO: Remove when we have better headers
|
2016-12-26 23:51:17 +01:00
|
|
|
GL_SRC1_COLOR,
|
|
|
|
GL_ONE_MINUS_SRC1_COLOR,
|
|
|
|
GL_SRC1_ALPHA,
|
|
|
|
GL_ONE_MINUS_SRC1_ALPHA,
|
2016-12-26 23:59:42 +01:00
|
|
|
#elif !defined(IOS)
|
|
|
|
GL_SRC1_COLOR_EXT,
|
|
|
|
GL_ONE_MINUS_SRC1_COLOR_EXT,
|
|
|
|
GL_SRC1_ALPHA_EXT,
|
|
|
|
GL_ONE_MINUS_SRC1_ALPHA_EXT,
|
|
|
|
#else
|
|
|
|
GL_INVALID_ENUM,
|
|
|
|
GL_INVALID_ENUM,
|
|
|
|
GL_INVALID_ENUM,
|
|
|
|
GL_INVALID_ENUM,
|
|
|
|
#endif
|
2014-08-17 12:16:57 +02:00
|
|
|
};
|
|
|
|
|
2016-02-13 13:37:00 -08:00
|
|
|
static const unsigned short texWrapToGL[] = {
|
|
|
|
GL_REPEAT,
|
2016-12-26 23:51:17 +01:00
|
|
|
GL_MIRRORED_REPEAT,
|
2016-02-13 13:37:00 -08:00
|
|
|
GL_CLAMP_TO_EDGE,
|
2016-12-26 23:59:42 +01:00
|
|
|
#if !defined(USING_GLES2)
|
2016-12-26 23:51:17 +01:00
|
|
|
GL_CLAMP_TO_BORDER,
|
2016-12-26 23:59:42 +01:00
|
|
|
#else
|
2017-02-19 11:22:23 +01:00
|
|
|
GL_CLAMP_TO_EDGE,
|
2016-12-26 23:59:42 +01:00
|
|
|
#endif
|
2016-02-13 13:37:00 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
static const unsigned short texFilterToGL[] = {
|
|
|
|
GL_NEAREST,
|
|
|
|
GL_LINEAR,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const unsigned short texMipFilterToGL[2][2] = {
|
|
|
|
// Min nearest:
|
|
|
|
{ GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR },
|
|
|
|
// Min linear:
|
|
|
|
{ GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR },
|
|
|
|
};
|
|
|
|
|
2015-01-04 17:59:58 +01:00
|
|
|
#ifndef USING_GLES2
|
2015-01-04 17:57:52 +01:00
|
|
|
static const unsigned short logicOpToGL[] = {
|
|
|
|
GL_CLEAR,
|
|
|
|
GL_SET,
|
|
|
|
GL_COPY,
|
|
|
|
GL_COPY_INVERTED,
|
|
|
|
GL_NOOP,
|
|
|
|
GL_INVERT,
|
|
|
|
GL_AND,
|
|
|
|
GL_NAND,
|
|
|
|
GL_OR,
|
|
|
|
GL_NOR,
|
|
|
|
GL_XOR,
|
|
|
|
GL_EQUIV,
|
|
|
|
GL_AND_REVERSE,
|
|
|
|
GL_AND_INVERTED,
|
|
|
|
GL_OR_REVERSE,
|
|
|
|
GL_OR_INVERTED,
|
|
|
|
};
|
2015-01-04 17:59:58 +01:00
|
|
|
#endif
|
2015-01-04 17:57:52 +01:00
|
|
|
|
2016-12-26 23:11:31 +01:00
|
|
|
static const GLuint stencilOpToGL[8] = {
|
|
|
|
GL_KEEP,
|
|
|
|
GL_ZERO,
|
|
|
|
GL_REPLACE,
|
|
|
|
GL_INCR,
|
|
|
|
GL_DECR,
|
|
|
|
GL_INVERT,
|
|
|
|
GL_INCR_WRAP,
|
|
|
|
GL_DECR_WRAP,
|
|
|
|
};
|
|
|
|
|
2014-08-17 12:16:57 +02:00
|
|
|
static const unsigned short primToGL[] = {
|
|
|
|
GL_POINTS,
|
|
|
|
GL_LINES,
|
2016-12-25 21:01:57 +01:00
|
|
|
GL_LINE_STRIP,
|
2014-08-17 12:16:57 +02:00
|
|
|
GL_TRIANGLES,
|
2016-12-25 21:01:57 +01:00
|
|
|
GL_TRIANGLE_STRIP,
|
|
|
|
GL_TRIANGLE_FAN,
|
2016-12-26 23:59:42 +01:00
|
|
|
#if !defined(USING_GLES2) // TODO: Remove when we have better headers
|
2016-12-25 21:01:57 +01:00
|
|
|
GL_PATCHES,
|
|
|
|
GL_LINES_ADJACENCY,
|
|
|
|
GL_LINE_STRIP_ADJACENCY,
|
|
|
|
GL_TRIANGLES_ADJACENCY,
|
|
|
|
GL_TRIANGLE_STRIP_ADJACENCY,
|
2016-12-26 23:59:42 +01:00
|
|
|
#elif !defined(IOS)
|
|
|
|
GL_POINTS,
|
|
|
|
GL_POINTS,
|
|
|
|
GL_POINTS,
|
|
|
|
GL_POINTS,
|
|
|
|
GL_POINTS,
|
|
|
|
#else
|
|
|
|
GL_POINTS,
|
|
|
|
GL_POINTS,
|
|
|
|
GL_POINTS,
|
|
|
|
GL_POINTS,
|
|
|
|
GL_POINTS,
|
|
|
|
#endif
|
2014-08-17 12:16:57 +02:00
|
|
|
};
|
|
|
|
|
2017-02-07 19:04:44 +01:00
|
|
|
class OpenGLBuffer;
|
|
|
|
|
2014-08-17 12:16:57 +02:00
|
|
|
static const char *glsl_fragment_prelude =
|
|
|
|
"#ifdef GL_ES\n"
|
|
|
|
"precision mediump float;\n"
|
|
|
|
"#endif\n";
|
|
|
|
|
2016-12-25 21:21:56 +01:00
|
|
|
class OpenGLBlendState : public BlendState {
|
2014-08-17 12:16:57 +02:00
|
|
|
public:
|
|
|
|
bool enabled;
|
|
|
|
GLuint eqCol, eqAlpha;
|
|
|
|
GLuint srcCol, srcAlpha, dstCol, dstAlpha;
|
2015-01-04 17:57:52 +01:00
|
|
|
bool logicEnabled;
|
|
|
|
GLuint logicOp;
|
2016-12-26 18:32:52 +01:00
|
|
|
int colorMask;
|
2014-08-23 10:31:23 +02:00
|
|
|
// uint32_t fixedColor;
|
2014-08-17 12:16:57 +02:00
|
|
|
|
|
|
|
void Apply() {
|
2015-09-06 12:21:15 +02:00
|
|
|
if (enabled) {
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendEquationSeparate(eqCol, eqAlpha);
|
|
|
|
glBlendFuncSeparate(srcCol, dstCol, srcAlpha, dstAlpha);
|
|
|
|
} else {
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
}
|
2016-12-26 18:32:52 +01:00
|
|
|
glColorMask(colorMask & 1, (colorMask >> 1) & 1, (colorMask >> 2) & 1, (colorMask >> 3) & 1);
|
|
|
|
|
2014-08-17 12:16:57 +02:00
|
|
|
#if !defined(USING_GLES2)
|
2015-01-04 17:57:52 +01:00
|
|
|
if (logicEnabled) {
|
2015-12-22 23:51:50 -08:00
|
|
|
glEnable(GL_COLOR_LOGIC_OP);
|
2015-09-06 12:21:15 +02:00
|
|
|
glLogicOp(logicOp);
|
|
|
|
} else {
|
2015-12-22 23:51:50 -08:00
|
|
|
glDisable(GL_COLOR_LOGIC_OP);
|
2015-01-04 17:57:52 +01:00
|
|
|
}
|
2014-08-17 12:16:57 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-12-25 21:21:56 +01:00
|
|
|
class OpenGLSamplerState : public SamplerState {
|
2016-02-13 13:37:00 -08:00
|
|
|
public:
|
2017-02-19 11:22:23 +01:00
|
|
|
GLint wrapU;
|
|
|
|
GLint wrapV;
|
|
|
|
GLint wrapW;
|
2016-02-13 13:37:00 -08:00
|
|
|
GLint magFilt;
|
|
|
|
GLint minFilt;
|
|
|
|
GLint mipMinFilt;
|
|
|
|
};
|
|
|
|
|
2016-12-25 21:21:56 +01:00
|
|
|
class OpenGLDepthStencilState : public DepthStencilState {
|
2014-08-17 12:16:57 +02:00
|
|
|
public:
|
|
|
|
bool depthTestEnabled;
|
|
|
|
bool depthWriteEnabled;
|
|
|
|
GLuint depthComp;
|
2016-12-26 23:11:31 +01:00
|
|
|
// TODO: Two-sided
|
|
|
|
GLboolean stencilEnabled;
|
|
|
|
GLuint stencilFail;
|
|
|
|
GLuint stencilZFail;
|
|
|
|
GLuint stencilPass;
|
|
|
|
GLuint stencilCompareOp;
|
|
|
|
uint8_t stencilReference;
|
|
|
|
uint8_t stencilCompareMask;
|
|
|
|
uint8_t stencilWriteMask;
|
2014-08-17 12:16:57 +02:00
|
|
|
|
|
|
|
void Apply() {
|
2015-09-06 12:21:15 +02:00
|
|
|
if (depthTestEnabled) {
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glDepthFunc(depthComp);
|
|
|
|
glDepthMask(depthWriteEnabled);
|
|
|
|
} else {
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
}
|
2016-12-26 23:11:31 +01:00
|
|
|
if (stencilEnabled) {
|
|
|
|
glEnable(GL_STENCIL_TEST);
|
|
|
|
glStencilOpSeparate(GL_FRONT_AND_BACK, stencilFail, stencilZFail, stencilPass);
|
|
|
|
glStencilFuncSeparate(GL_FRONT_AND_BACK, stencilCompareOp, stencilReference, stencilCompareMask);
|
|
|
|
glStencilMaskSeparate(GL_FRONT_AND_BACK, stencilWriteMask);
|
|
|
|
} else {
|
|
|
|
glDisable(GL_STENCIL_TEST);
|
|
|
|
}
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-12-25 21:21:56 +01:00
|
|
|
class OpenGLRasterState : public RasterState {
|
2016-12-23 23:46:11 +01:00
|
|
|
public:
|
|
|
|
void Apply() {
|
2017-01-17 20:26:19 +07:00
|
|
|
glEnable(GL_SCISSOR_TEST);
|
2016-12-23 23:46:11 +01:00
|
|
|
if (!cullEnable) {
|
|
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
glFrontFace(frontFace);
|
|
|
|
glCullFace(cullMode);
|
|
|
|
}
|
|
|
|
|
|
|
|
GLboolean cullEnable;
|
|
|
|
GLenum cullMode;
|
|
|
|
GLenum frontFace;
|
|
|
|
};
|
|
|
|
|
2016-12-25 22:24:14 +01:00
|
|
|
GLuint ShaderStageToOpenGL(ShaderStage stage) {
|
|
|
|
switch (stage) {
|
|
|
|
case ShaderStage::VERTEX: return GL_VERTEX_SHADER;
|
2016-12-26 23:59:42 +01:00
|
|
|
#ifndef USING_GLES2
|
2016-12-25 22:24:14 +01:00
|
|
|
case ShaderStage::COMPUTE: return GL_COMPUTE_SHADER;
|
|
|
|
case ShaderStage::EVALUATION: return GL_TESS_EVALUATION_SHADER;
|
|
|
|
case ShaderStage::CONTROL: return GL_TESS_CONTROL_SHADER;
|
|
|
|
case ShaderStage::GEOMETRY: return GL_GEOMETRY_SHADER;
|
2016-12-26 23:59:42 +01:00
|
|
|
#endif
|
2016-12-25 22:24:14 +01:00
|
|
|
case ShaderStage::FRAGMENT:
|
|
|
|
default:
|
|
|
|
return GL_FRAGMENT_SHADER;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-18 15:20:36 +01:00
|
|
|
class OpenGLShaderModule : public ShaderModule, public GfxResourceHolder {
|
2014-08-17 12:16:57 +02:00
|
|
|
public:
|
2017-03-18 15:20:36 +01:00
|
|
|
OpenGLShaderModule(ShaderStage stage) : stage_(stage) {
|
|
|
|
register_gl_resource_holder(this, "drawcontext_shader_module", 0);
|
2016-12-25 22:24:14 +01:00
|
|
|
glstage_ = ShaderStageToOpenGL(stage);
|
|
|
|
}
|
|
|
|
|
|
|
|
~OpenGLShaderModule() {
|
2017-03-18 15:20:36 +01:00
|
|
|
ILOG("Shader module destroyed");
|
|
|
|
if (shader_)
|
|
|
|
glDeleteShader(shader_);
|
|
|
|
unregister_gl_resource_holder(this);
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
|
|
|
|
2016-12-27 17:38:26 +01:00
|
|
|
bool Compile(ShaderLanguage language, const uint8_t *data, size_t dataSize);
|
2014-09-06 13:49:01 +02:00
|
|
|
GLuint GetShader() const {
|
|
|
|
return shader_;
|
|
|
|
}
|
|
|
|
const std::string &GetSource() const { return source_; }
|
2014-08-17 12:16:57 +02:00
|
|
|
|
2016-12-27 17:38:26 +01:00
|
|
|
ShaderLanguage GetLanguage() {
|
|
|
|
return language_;
|
|
|
|
}
|
2016-12-25 22:24:14 +01:00
|
|
|
ShaderStage GetStage() const override {
|
|
|
|
return stage_;
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
|
|
|
|
2017-03-18 15:20:36 +01:00
|
|
|
void GLLost() override {
|
|
|
|
ILOG("Shader module lost");
|
|
|
|
// Shader has been destroyed since the old context is gone, so let's zero it.
|
|
|
|
shader_ = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLRestore() override {
|
|
|
|
ILOG("Shader module being restored");
|
|
|
|
if (!Compile(language_, (const uint8_t *)source_.data(), source_.size())) {
|
|
|
|
ELOG("Shader restore compilation failed: %s", source_.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-17 12:16:57 +02:00
|
|
|
private:
|
2016-12-25 22:24:14 +01:00
|
|
|
ShaderStage stage_;
|
2016-12-27 17:38:26 +01:00
|
|
|
ShaderLanguage language_;
|
2017-03-18 15:20:36 +01:00
|
|
|
GLuint shader_ = 0;
|
|
|
|
GLuint glstage_ = 0;
|
|
|
|
bool ok_ = false;
|
2014-09-06 13:49:01 +02:00
|
|
|
std::string source_; // So we can recompile in case of context loss.
|
2014-08-17 12:16:57 +02:00
|
|
|
};
|
|
|
|
|
2016-12-27 17:38:26 +01:00
|
|
|
bool OpenGLShaderModule::Compile(ShaderLanguage language, const uint8_t *data, size_t dataSize) {
|
|
|
|
source_ = std::string((const char *)data);
|
2016-12-25 22:24:14 +01:00
|
|
|
shader_ = glCreateShader(glstage_);
|
2016-12-27 17:38:26 +01:00
|
|
|
language_ = language;
|
2014-08-23 09:25:59 -07:00
|
|
|
|
|
|
|
std::string temp;
|
|
|
|
// Add the prelude on automatically for fragment shaders.
|
2016-12-25 22:24:14 +01:00
|
|
|
if (glstage_ == GL_FRAGMENT_SHADER) {
|
2016-12-27 17:38:26 +01:00
|
|
|
temp = std::string(glsl_fragment_prelude) + source_;
|
|
|
|
source_ = temp.c_str();
|
2014-08-23 09:25:59 -07:00
|
|
|
}
|
|
|
|
|
2016-12-27 17:38:26 +01:00
|
|
|
const char *code = source_.c_str();
|
|
|
|
glShaderSource(shader_, 1, &code, nullptr);
|
2014-08-17 12:16:57 +02:00
|
|
|
glCompileShader(shader_);
|
2014-11-23 11:01:51 -08:00
|
|
|
GLint success = 0;
|
2014-08-17 12:16:57 +02:00
|
|
|
glGetShaderiv(shader_, GL_COMPILE_STATUS, &success);
|
|
|
|
if (!success) {
|
|
|
|
#define MAX_INFO_LOG_SIZE 2048
|
|
|
|
GLchar infoLog[MAX_INFO_LOG_SIZE];
|
2014-11-23 11:01:51 -08:00
|
|
|
GLsizei len = 0;
|
2014-08-17 12:16:57 +02:00
|
|
|
glGetShaderInfoLog(shader_, MAX_INFO_LOG_SIZE, &len, infoLog);
|
|
|
|
infoLog[len] = '\0';
|
|
|
|
glDeleteShader(shader_);
|
|
|
|
shader_ = 0;
|
2016-12-25 22:24:14 +01:00
|
|
|
ILOG("%s Shader compile error:\n%s", glstage_ == GL_FRAGMENT_SHADER ? "Fragment" : "Vertex", infoLog);
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
|
|
|
ok_ = success != 0;
|
|
|
|
return ok_;
|
|
|
|
}
|
|
|
|
|
2016-12-26 13:22:48 +01:00
|
|
|
class OpenGLInputLayout : public InputLayout, GfxResourceHolder {
|
2014-08-17 12:16:57 +02:00
|
|
|
public:
|
2016-12-26 13:22:48 +01:00
|
|
|
~OpenGLInputLayout();
|
2015-12-13 09:25:22 -08:00
|
|
|
|
2015-01-29 10:35:34 +01:00
|
|
|
void Apply(const void *base = nullptr);
|
2014-08-17 12:16:57 +02:00
|
|
|
void Unapply();
|
|
|
|
void Compile();
|
2016-03-17 21:30:16 +01:00
|
|
|
void GLRestore() override;
|
2016-09-10 20:29:58 -07:00
|
|
|
void GLLost() override;
|
2016-12-26 17:03:01 +01:00
|
|
|
bool RequiresBuffer() {
|
2015-12-13 09:25:22 -08:00
|
|
|
return id_ != 0;
|
|
|
|
}
|
2014-08-17 12:16:57 +02:00
|
|
|
|
2016-12-26 13:22:48 +01:00
|
|
|
InputLayoutDesc desc;
|
|
|
|
|
2014-08-17 12:16:57 +02:00
|
|
|
int semanticsMask_; // Fast way to check what semantics to enable/disable.
|
|
|
|
int stride_;
|
2015-12-13 09:25:22 -08:00
|
|
|
GLuint id_;
|
|
|
|
bool needsEnable_;
|
|
|
|
intptr_t lastBase_;
|
2014-08-17 12:16:57 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
struct UniformInfo {
|
|
|
|
int loc_;
|
|
|
|
};
|
|
|
|
|
2016-12-26 11:06:17 +01:00
|
|
|
class OpenGLPipeline : public Pipeline, GfxResourceHolder {
|
2014-08-17 12:16:57 +02:00
|
|
|
public:
|
2016-12-26 11:06:17 +01:00
|
|
|
OpenGLPipeline() {
|
2014-09-06 13:49:01 +02:00
|
|
|
program_ = 0;
|
2017-03-18 15:20:36 +01:00
|
|
|
// Priority 1 so this gets restored after the shaders.
|
|
|
|
register_gl_resource_holder(this, "drawcontext_pipeline", 1);
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
2016-12-26 11:06:17 +01:00
|
|
|
~OpenGLPipeline() {
|
2014-09-06 13:49:01 +02:00
|
|
|
unregister_gl_resource_holder(this);
|
2017-03-18 15:20:36 +01:00
|
|
|
for (auto &iter : shaders) {
|
2016-12-25 23:03:20 +01:00
|
|
|
iter->Release();
|
|
|
|
}
|
2014-08-17 12:16:57 +02:00
|
|
|
glDeleteProgram(program_);
|
2016-12-26 17:03:01 +01:00
|
|
|
if (depthStencil) depthStencil->Release();
|
|
|
|
if (blend) blend->Release();
|
|
|
|
if (raster) raster->Release();
|
|
|
|
if (inputLayout) inputLayout->Release();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool LinkShaders();
|
2015-09-17 20:29:37 +02:00
|
|
|
|
2014-08-17 12:16:57 +02:00
|
|
|
int GetUniformLoc(const char *name);
|
|
|
|
|
2016-09-10 20:29:58 -07:00
|
|
|
void GLLost() override {
|
|
|
|
program_ = 0;
|
|
|
|
}
|
|
|
|
|
2016-03-17 21:30:16 +01:00
|
|
|
void GLRestore() override {
|
2017-03-18 15:20:36 +01:00
|
|
|
// Shaders will have been restored before the pipeline.
|
2016-12-26 17:03:01 +01:00
|
|
|
LinkShaders();
|
2014-09-06 13:49:01 +02:00
|
|
|
}
|
|
|
|
|
2017-02-08 13:07:38 +01:00
|
|
|
bool RequiresBuffer() override {
|
|
|
|
return inputLayout->RequiresBuffer();
|
|
|
|
}
|
|
|
|
|
2016-12-26 17:03:01 +01:00
|
|
|
GLuint prim;
|
2016-12-25 23:03:20 +01:00
|
|
|
std::vector<OpenGLShaderModule *> shaders;
|
2016-12-26 17:03:01 +01:00
|
|
|
OpenGLInputLayout *inputLayout = nullptr;
|
|
|
|
OpenGLDepthStencilState *depthStencil = nullptr;
|
|
|
|
OpenGLBlendState *blend = nullptr;
|
|
|
|
OpenGLRasterState *raster = nullptr;
|
2014-08-17 12:16:57 +02:00
|
|
|
|
2017-02-08 12:26:48 +01:00
|
|
|
// TODO: Optimize by getting the locations first and putting in a custom struct
|
|
|
|
UniformBufferDesc dynamicUniforms;
|
|
|
|
|
2014-08-17 12:16:57 +02:00
|
|
|
GLuint program_;
|
2017-02-08 12:55:58 +01:00
|
|
|
private:
|
2017-02-08 12:26:48 +01:00
|
|
|
std::map<std::string, UniformInfo> uniformCache_;
|
2014-08-17 12:16:57 +02:00
|
|
|
};
|
|
|
|
|
2017-02-04 18:46:12 +01:00
|
|
|
class OpenGLFramebuffer;
|
2017-02-19 11:22:23 +01:00
|
|
|
class OpenGLTexture;
|
2017-02-04 18:46:12 +01:00
|
|
|
|
2016-12-25 21:21:56 +01:00
|
|
|
class OpenGLContext : public DrawContext {
|
2014-08-17 12:16:57 +02:00
|
|
|
public:
|
2016-12-25 21:21:56 +01:00
|
|
|
OpenGLContext();
|
|
|
|
virtual ~OpenGLContext();
|
2014-08-17 12:16:57 +02:00
|
|
|
|
2016-12-26 17:31:20 +01:00
|
|
|
const DeviceCaps &GetDeviceCaps() const override {
|
|
|
|
return caps_;
|
|
|
|
}
|
2016-12-27 16:33:54 +01:00
|
|
|
uint32_t GetSupportedShaderLanguages() const override {
|
|
|
|
#if defined(USING_GLES2)
|
|
|
|
return (uint32_t)ShaderLanguage::GLSL_ES_200 | (uint32_t)ShaderLanguage::GLSL_ES_300;
|
|
|
|
#else
|
2016-12-27 17:38:26 +01:00
|
|
|
return (uint32_t)ShaderLanguage::GLSL_ES_200 | (uint32_t)ShaderLanguage::GLSL_410;
|
2016-12-27 16:33:54 +01:00
|
|
|
#endif
|
|
|
|
}
|
2017-01-19 12:16:36 +07:00
|
|
|
uint32_t GetDataFormatSupport(DataFormat fmt) const override;
|
2016-12-26 17:31:20 +01:00
|
|
|
|
2016-12-25 21:10:46 +01:00
|
|
|
DepthStencilState *CreateDepthStencilState(const DepthStencilStateDesc &desc) override;
|
2016-12-25 18:52:05 +01:00
|
|
|
BlendState *CreateBlendState(const BlendStateDesc &desc) override;
|
2016-12-25 20:54:37 +01:00
|
|
|
SamplerState *CreateSamplerState(const SamplerStateDesc &desc) override;
|
2016-12-25 22:24:14 +01:00
|
|
|
RasterState *CreateRasterState(const RasterStateDesc &desc) override;
|
2016-12-26 13:42:53 +01:00
|
|
|
Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc) override;
|
2016-12-26 13:22:48 +01:00
|
|
|
InputLayout *CreateInputLayout(const InputLayoutDesc &desc) override;
|
2016-12-27 17:38:26 +01:00
|
|
|
ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize) override;
|
2016-12-27 16:33:54 +01:00
|
|
|
|
2017-01-16 23:43:07 +07:00
|
|
|
Texture *CreateTexture(const TextureDesc &desc) override;
|
2017-02-04 18:46:12 +01:00
|
|
|
Buffer *CreateBuffer(size_t size, uint32_t usageFlags) override;
|
2017-02-06 11:26:24 +01:00
|
|
|
Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) override;
|
2017-02-04 18:46:12 +01:00
|
|
|
|
2017-02-07 19:41:58 +01:00
|
|
|
void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override;
|
2017-02-07 18:16:52 +01:00
|
|
|
|
2017-02-12 11:20:55 +01:00
|
|
|
void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) override;
|
2017-02-06 11:26:24 +01:00
|
|
|
bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) override;
|
2017-02-04 18:46:12 +01:00
|
|
|
|
|
|
|
// These functions should be self explanatory.
|
2017-05-16 16:00:34 +02:00
|
|
|
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) override;
|
2017-02-04 18:46:12 +01:00
|
|
|
// color must be 0, for now.
|
2017-02-06 11:26:24 +01:00
|
|
|
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) override;
|
|
|
|
void BindFramebufferForRead(Framebuffer *fbo) override;
|
2017-02-04 18:46:12 +01:00
|
|
|
|
2017-02-06 11:26:24 +01:00
|
|
|
uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBits, int attachment) override;
|
2017-02-04 18:46:12 +01:00
|
|
|
|
2017-02-06 11:26:24 +01:00
|
|
|
void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override;
|
2014-08-17 12:16:57 +02:00
|
|
|
|
2016-12-26 11:16:59 +01:00
|
|
|
void BindSamplerStates(int start, int count, SamplerState **states) override {
|
2017-02-19 11:22:23 +01:00
|
|
|
if (boundSamplers_.size() < (size_t)(start + count)) {
|
|
|
|
boundSamplers_.resize(start + count);
|
2016-02-13 13:37:00 -08:00
|
|
|
}
|
2017-02-19 11:22:23 +01:00
|
|
|
for (int i = 0; i < count; i++) {
|
2016-02-13 13:37:00 -08:00
|
|
|
int index = i + start;
|
2017-02-19 11:22:23 +01:00
|
|
|
boundSamplers_[index] = static_cast<OpenGLSamplerState *>(states[index]);
|
2016-02-13 13:37:00 -08:00
|
|
|
}
|
2016-12-23 23:46:11 +01:00
|
|
|
}
|
|
|
|
|
2014-08-17 12:16:57 +02:00
|
|
|
void SetScissorRect(int left, int top, int width, int height) override {
|
2015-09-06 12:21:15 +02:00
|
|
|
glScissor(left, targetHeight_ - (top + height), width, height);
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
|
|
|
|
2016-12-25 18:52:05 +01:00
|
|
|
void SetViewports(int count, Viewport *viewports) override {
|
2016-12-27 22:26:49 +01:00
|
|
|
// TODO: Use glViewportArrayv.
|
2017-02-08 12:55:58 +01:00
|
|
|
glViewport((GLint)viewports[0].TopLeftX, (GLint)viewports[0].TopLeftY, (GLsizei)viewports[0].Width, (GLsizei)viewports[0].Height);
|
2015-09-06 12:21:15 +02:00
|
|
|
#if defined(USING_GLES2)
|
|
|
|
glDepthRangef(viewports[0].MinDepth, viewports[0].MaxDepth);
|
|
|
|
#else
|
|
|
|
glDepthRange(viewports[0].MinDepth, viewports[0].MaxDepth);
|
|
|
|
#endif
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
|
|
|
|
2016-12-27 15:52:03 +01:00
|
|
|
void SetBlendFactor(float color[4]) override {
|
|
|
|
glBlendColor(color[0], color[1], color[2], color[3]);
|
|
|
|
}
|
|
|
|
|
2016-12-25 20:54:37 +01:00
|
|
|
void BindTextures(int start, int count, Texture **textures) override;
|
2016-12-26 17:03:01 +01:00
|
|
|
void BindPipeline(Pipeline *pipeline) override;
|
2017-01-17 23:14:47 +07:00
|
|
|
void BindVertexBuffers(int start, int count, Buffer **buffers, int *offsets) override {
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
curVBuffers_[i + start] = (OpenGLBuffer *)buffers[i];
|
|
|
|
curVBufferOffsets_[i + start] = offsets ? offsets[i] : 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void BindIndexBuffer(Buffer *indexBuffer, int offset) override {
|
|
|
|
curIBuffer_ = (OpenGLBuffer *)indexBuffer;
|
|
|
|
curIBufferOffset_ = offset;
|
|
|
|
}
|
2014-08-17 15:02:43 +02:00
|
|
|
|
2017-02-19 11:22:23 +01:00
|
|
|
void UpdateDynamicUniformBuffer(const void *ub, size_t size) override;
|
2017-02-08 12:26:48 +01:00
|
|
|
|
2014-08-17 12:16:57 +02:00
|
|
|
// TODO: Add more sophisticated draws.
|
2017-01-17 23:14:47 +07:00
|
|
|
void Draw(int vertexCount, int offset) override;
|
|
|
|
void DrawIndexed(int vertexCount, int offset) override;
|
2016-12-26 17:03:01 +01:00
|
|
|
void DrawUP(const void *vdata, int vertexCount) override;
|
2017-01-17 23:14:47 +07:00
|
|
|
|
2014-08-17 12:16:57 +02:00
|
|
|
void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) override;
|
2014-08-22 19:55:39 +02:00
|
|
|
|
2016-12-25 18:52:05 +01:00
|
|
|
std::string GetInfoString(InfoField info) const override {
|
2014-08-22 20:47:40 +02:00
|
|
|
// TODO: Make these actually query the right information
|
|
|
|
switch (info) {
|
|
|
|
case APINAME:
|
2015-08-23 21:34:11 -07:00
|
|
|
if (gl_extensions.IsGLES) {
|
|
|
|
return "OpenGL ES";
|
|
|
|
} else {
|
|
|
|
return "OpenGL";
|
|
|
|
}
|
2015-07-21 20:46:31 +02:00
|
|
|
case VENDORSTRING: return (const char *)glGetString(GL_VENDOR);
|
|
|
|
case VENDOR:
|
|
|
|
switch (gl_extensions.gpuVendor) {
|
|
|
|
case GPU_VENDOR_AMD: return "VENDOR_AMD";
|
|
|
|
case GPU_VENDOR_POWERVR: return "VENDOR_POWERVR";
|
|
|
|
case GPU_VENDOR_NVIDIA: return "VENDOR_NVIDIA";
|
|
|
|
case GPU_VENDOR_INTEL: return "VENDOR_INTEL";
|
|
|
|
case GPU_VENDOR_ADRENO: return "VENDOR_ADRENO";
|
|
|
|
case GPU_VENDOR_ARM: return "VENDOR_ARM";
|
|
|
|
case GPU_VENDOR_BROADCOM: return "VENDOR_BROADCOM";
|
|
|
|
case GPU_VENDOR_UNKNOWN:
|
|
|
|
default:
|
|
|
|
return "VENDOR_UNKNOWN";
|
|
|
|
}
|
|
|
|
break;
|
2017-03-16 09:48:10 +01:00
|
|
|
case DRIVER: return (const char *)glGetString(GL_RENDERER);
|
2014-08-22 20:47:40 +02:00
|
|
|
case SHADELANGVERSION: return (const char *)glGetString(GL_SHADING_LANGUAGE_VERSION);
|
|
|
|
case APIVERSION: return (const char *)glGetString(GL_VERSION);
|
|
|
|
default: return "?";
|
|
|
|
}
|
2014-08-22 19:55:39 +02:00
|
|
|
}
|
2016-02-13 13:37:00 -08:00
|
|
|
|
2017-02-05 20:05:03 +01:00
|
|
|
uintptr_t GetNativeObject(NativeObject obj) const override {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-03-05 20:30:39 +01:00
|
|
|
void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override {}
|
2017-02-06 11:20:27 +01:00
|
|
|
|
2017-02-19 11:22:23 +01:00
|
|
|
private:
|
2017-02-04 18:46:12 +01:00
|
|
|
OpenGLFramebuffer *fbo_ext_create(const FramebufferDesc &desc);
|
|
|
|
void fbo_bind_fb_target(bool read, GLuint name);
|
|
|
|
GLenum fbo_get_fb_target(bool read, GLuint **cached);
|
|
|
|
void fbo_unbind();
|
2017-02-19 11:22:23 +01:00
|
|
|
void ApplySamplers();
|
2017-02-04 18:46:12 +01:00
|
|
|
|
2017-02-19 11:22:23 +01:00
|
|
|
std::vector<OpenGLSamplerState *> boundSamplers_;
|
|
|
|
OpenGLTexture *boundTextures_[8]{};
|
|
|
|
int maxTextures_ = 0;
|
2017-02-20 11:21:07 +01:00
|
|
|
DeviceCaps caps_{};
|
2017-01-17 23:14:47 +07:00
|
|
|
|
|
|
|
// Bound state
|
2017-02-20 11:21:07 +01:00
|
|
|
OpenGLPipeline *curPipeline_ = nullptr;
|
|
|
|
OpenGLBuffer *curVBuffers_[4]{};
|
|
|
|
int curVBufferOffsets_[4]{};
|
|
|
|
OpenGLBuffer *curIBuffer_ = nullptr;
|
|
|
|
int curIBufferOffset_ = 0;
|
2017-02-04 18:46:12 +01:00
|
|
|
|
|
|
|
// Framebuffer state
|
|
|
|
GLuint currentDrawHandle_ = 0;
|
|
|
|
GLuint currentReadHandle_ = 0;
|
2014-08-17 12:16:57 +02:00
|
|
|
};
|
|
|
|
|
2016-12-25 21:21:56 +01:00
|
|
|
OpenGLContext::OpenGLContext() {
|
2014-08-17 12:16:57 +02:00
|
|
|
CreatePresets();
|
2017-02-06 11:35:55 +01:00
|
|
|
|
|
|
|
// TODO: Detect more caps
|
|
|
|
if (gl_extensions.IsGLES) {
|
|
|
|
if (gl_extensions.OES_packed_depth_stencil || gl_extensions.OES_depth24) {
|
|
|
|
caps_.preferredDepthBufferFormat = DataFormat::D24_S8;
|
|
|
|
} else {
|
|
|
|
caps_.preferredDepthBufferFormat = DataFormat::D16;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
caps_.preferredDepthBufferFormat = DataFormat::D24_S8;
|
|
|
|
}
|
2017-02-12 18:29:58 +01:00
|
|
|
caps_.framebufferBlitSupported = gl_extensions.NV_framebuffer_blit || gl_extensions.ARB_framebuffer_object;
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
|
|
|
|
2016-12-25 21:21:56 +01:00
|
|
|
OpenGLContext::~OpenGLContext() {
|
2017-02-19 11:22:23 +01:00
|
|
|
boundSamplers_.clear();
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
|
|
|
|
2016-12-26 13:22:48 +01:00
|
|
|
InputLayout *OpenGLContext::CreateInputLayout(const InputLayoutDesc &desc) {
|
|
|
|
OpenGLInputLayout *fmt = new OpenGLInputLayout();
|
|
|
|
fmt->desc = desc;
|
2014-08-17 12:16:57 +02:00
|
|
|
fmt->Compile();
|
|
|
|
return fmt;
|
|
|
|
}
|
|
|
|
|
2016-12-25 18:52:05 +01:00
|
|
|
GLuint TypeToTarget(TextureType type) {
|
2014-08-17 15:02:43 +02:00
|
|
|
switch (type) {
|
2014-08-23 00:34:55 -07:00
|
|
|
#ifndef USING_GLES2
|
2017-01-16 23:43:07 +07:00
|
|
|
case TextureType::LINEAR1D: return GL_TEXTURE_1D;
|
2014-08-23 00:34:55 -07:00
|
|
|
#endif
|
2017-01-16 23:43:07 +07:00
|
|
|
case TextureType::LINEAR2D: return GL_TEXTURE_2D;
|
|
|
|
case TextureType::LINEAR3D: return GL_TEXTURE_3D;
|
|
|
|
case TextureType::CUBE: return GL_TEXTURE_CUBE_MAP;
|
2014-08-23 00:34:55 -07:00
|
|
|
#ifndef USING_GLES2
|
2017-01-16 23:43:07 +07:00
|
|
|
case TextureType::ARRAY1D: return GL_TEXTURE_1D_ARRAY;
|
2014-08-23 00:34:55 -07:00
|
|
|
#endif
|
2017-01-16 23:43:07 +07:00
|
|
|
case TextureType::ARRAY2D: return GL_TEXTURE_2D_ARRAY;
|
2017-02-19 11:22:23 +01:00
|
|
|
default:
|
|
|
|
ELOG("Bad texture type %d", (int)type);
|
|
|
|
return GL_NONE;
|
2014-08-17 15:02:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-18 00:31:44 +07:00
|
|
|
inline bool isPowerOf2(int n) {
|
|
|
|
return n == 1 || (n & (n - 1)) == 0;
|
|
|
|
}
|
|
|
|
|
2017-01-26 11:57:48 +01:00
|
|
|
class OpenGLTexture : public Texture {
|
2014-08-17 15:02:43 +02:00
|
|
|
public:
|
2017-02-19 11:02:24 +01:00
|
|
|
OpenGLTexture(const TextureDesc &desc);
|
|
|
|
~OpenGLTexture();
|
2017-01-16 23:43:07 +07:00
|
|
|
|
2017-02-19 11:22:23 +01:00
|
|
|
bool HasMips() const {
|
2016-02-13 13:37:00 -08:00
|
|
|
return mipLevels_ > 1 || generatedMips_;
|
|
|
|
}
|
2017-02-19 11:22:23 +01:00
|
|
|
bool CanWrap() const {
|
2016-02-13 13:37:00 -08:00
|
|
|
return canWrap_;
|
|
|
|
}
|
2017-02-19 11:22:23 +01:00
|
|
|
TextureType GetType() const { return type_; }
|
2014-08-17 21:28:34 +02:00
|
|
|
void Bind() {
|
|
|
|
glBindTexture(target_, tex_);
|
|
|
|
}
|
2014-09-06 13:49:01 +02:00
|
|
|
|
2017-02-19 11:22:23 +01:00
|
|
|
void AutoGenMipmaps();
|
|
|
|
|
2014-08-17 15:02:43 +02:00
|
|
|
private:
|
2017-02-19 11:02:24 +01:00
|
|
|
void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data);
|
|
|
|
|
2017-02-19 11:22:23 +01:00
|
|
|
GLuint tex_ = 0;
|
|
|
|
GLuint target_ = 0;
|
2014-08-17 21:28:34 +02:00
|
|
|
|
2016-12-25 18:52:05 +01:00
|
|
|
DataFormat format_;
|
2017-02-19 11:22:23 +01:00
|
|
|
TextureType type_;
|
2014-08-17 21:28:34 +02:00
|
|
|
int mipLevels_;
|
2016-01-23 11:22:40 -08:00
|
|
|
bool generatedMips_;
|
2016-02-13 13:37:00 -08:00
|
|
|
bool canWrap_;
|
2014-08-17 15:02:43 +02:00
|
|
|
};
|
|
|
|
|
2017-02-19 11:22:23 +01:00
|
|
|
OpenGLTexture::OpenGLTexture(const TextureDesc &desc) {
|
2017-02-19 11:02:24 +01:00
|
|
|
generatedMips_ = false;
|
|
|
|
canWrap_ = true;
|
|
|
|
width_ = desc.width;
|
|
|
|
height_ = desc.height;
|
|
|
|
depth_ = desc.depth;
|
2017-02-19 11:22:23 +01:00
|
|
|
format_ = desc.format;
|
|
|
|
type_ = desc.type;
|
|
|
|
target_ = TypeToTarget(desc.type);
|
|
|
|
canWrap_ = isPowerOf2(width_) && isPowerOf2(height_);
|
2017-06-05 17:32:49 +02:00
|
|
|
mipLevels_ = desc.mipLevels;
|
2017-02-19 11:02:24 +01:00
|
|
|
if (!desc.initData.size())
|
|
|
|
return;
|
|
|
|
|
2017-02-19 11:22:23 +01:00
|
|
|
glActiveTexture(GL_TEXTURE0 + 0);
|
|
|
|
glGenTextures(1, &tex_);
|
|
|
|
glBindTexture(target_, tex_);
|
|
|
|
|
2017-02-19 11:02:24 +01:00
|
|
|
int level = 0;
|
|
|
|
for (auto data : desc.initData) {
|
|
|
|
SetImageData(0, 0, 0, width_, height_, depth_, level, 0, data);
|
|
|
|
width_ = (width_ + 1) / 2;
|
|
|
|
height_ = (height_ + 1) / 2;
|
|
|
|
level++;
|
|
|
|
}
|
2017-03-11 14:43:42 +01:00
|
|
|
mipLevels_ = desc.generateMips ? desc.mipLevels : level;
|
2017-02-19 11:02:24 +01:00
|
|
|
|
2017-02-19 11:09:42 +01:00
|
|
|
#ifdef USING_GLES2
|
|
|
|
if (gl_extensions.GLES3) {
|
2017-03-11 14:43:42 +01:00
|
|
|
glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, mipLevels_ - 1);
|
2017-02-19 11:09:42 +01:00
|
|
|
}
|
|
|
|
#else
|
2017-03-11 14:43:42 +01:00
|
|
|
glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, mipLevels_ - 1);
|
2017-02-19 11:09:42 +01:00
|
|
|
#endif
|
2017-03-11 14:43:42 +01:00
|
|
|
glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, mipLevels_ > 1 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
|
2017-02-19 11:22:23 +01:00
|
|
|
glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
2017-02-19 11:02:24 +01:00
|
|
|
|
2017-03-11 14:43:42 +01:00
|
|
|
if ((int)desc.initData.size() < desc.mipLevels && desc.generateMips) {
|
2017-02-19 11:22:23 +01:00
|
|
|
ILOG("Generating mipmaps");
|
2017-02-19 11:02:24 +01:00
|
|
|
AutoGenMipmaps();
|
2017-02-19 11:22:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Unbind.
|
|
|
|
glBindTexture(target_, 0);
|
2017-02-19 11:02:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
OpenGLTexture::~OpenGLTexture() {
|
|
|
|
if (tex_) {
|
|
|
|
glDeleteTextures(1, &tex_);
|
|
|
|
tex_ = 0;
|
|
|
|
generatedMips_ = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-16 23:34:53 +07:00
|
|
|
void OpenGLTexture::AutoGenMipmaps() {
|
2016-01-23 11:22:40 -08:00
|
|
|
if (!generatedMips_) {
|
2017-02-19 11:22:23 +01:00
|
|
|
glBindTexture(target_, tex_);
|
2016-01-23 11:22:40 -08:00
|
|
|
glGenerateMipmap(target_);
|
|
|
|
generatedMips_ = true;
|
|
|
|
}
|
2014-08-17 15:02:43 +02:00
|
|
|
}
|
|
|
|
|
2017-01-16 23:34:53 +07:00
|
|
|
void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) {
|
2014-08-17 15:02:43 +02:00
|
|
|
int internalFormat;
|
|
|
|
int format;
|
|
|
|
int type;
|
2017-01-28 16:55:23 +01:00
|
|
|
|
|
|
|
if (width != width_ || height != height_ || depth != depth_) {
|
|
|
|
// When switching to texStorage we need to handle this correctly.
|
|
|
|
width_ = width;
|
|
|
|
height_ = height;
|
|
|
|
depth_ = depth;
|
|
|
|
}
|
|
|
|
|
2014-08-17 15:02:43 +02:00
|
|
|
switch (format_) {
|
2016-12-25 21:21:56 +01:00
|
|
|
case DataFormat::R8G8B8A8_UNORM:
|
2014-08-17 15:02:43 +02:00
|
|
|
internalFormat = GL_RGBA;
|
|
|
|
format = GL_RGBA;
|
|
|
|
type = GL_UNSIGNED_BYTE;
|
|
|
|
break;
|
2017-02-19 11:09:42 +01:00
|
|
|
case DataFormat::B4G4R4A4_UNORM_PACK16:
|
2014-08-22 20:47:40 +02:00
|
|
|
internalFormat = GL_RGBA;
|
|
|
|
format = GL_RGBA;
|
|
|
|
type = GL_UNSIGNED_SHORT_4_4_4_4;
|
|
|
|
break;
|
2017-01-29 14:19:58 +01:00
|
|
|
#ifndef USING_GLES2
|
2017-02-22 16:23:04 +01:00
|
|
|
case DataFormat::A4R4G4B4_UNORM_PACK16:
|
2017-01-21 13:11:03 +01:00
|
|
|
internalFormat = GL_RGBA;
|
|
|
|
format = GL_RGBA;
|
|
|
|
type = GL_UNSIGNED_SHORT_4_4_4_4_REV;
|
|
|
|
break;
|
|
|
|
#endif
|
2014-08-17 15:02:43 +02:00
|
|
|
default:
|
2017-01-29 14:19:58 +01:00
|
|
|
ELOG("Thin3d GL: Unsupported texture format %d", (int)format_);
|
2014-08-17 15:02:43 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-03-03 14:15:27 +01:00
|
|
|
CHECK_GL_ERROR_IF_DEBUG();
|
2014-08-17 15:02:43 +02:00
|
|
|
switch (target_) {
|
|
|
|
case GL_TEXTURE_2D:
|
2014-08-22 20:47:40 +02:00
|
|
|
glTexImage2D(GL_TEXTURE_2D, level, internalFormat, width_, height_, 0, format, type, data);
|
2014-08-17 15:02:43 +02:00
|
|
|
break;
|
2014-08-23 10:31:23 +02:00
|
|
|
default:
|
|
|
|
ELOG("Thin3D GL: Targets other than GL_TEXTURE_2D not yet supported");
|
|
|
|
break;
|
2014-08-17 15:02:43 +02:00
|
|
|
}
|
2017-03-03 14:15:27 +01:00
|
|
|
CHECK_GL_ERROR_IF_DEBUG();
|
2017-01-29 14:19:58 +01:00
|
|
|
}
|
2014-08-17 21:28:34 +02:00
|
|
|
|
2017-02-19 11:22:23 +01:00
|
|
|
Texture *OpenGLContext::CreateTexture(const TextureDesc &desc) {
|
|
|
|
return new OpenGLTexture(desc);
|
|
|
|
}
|
|
|
|
|
2016-12-26 13:22:48 +01:00
|
|
|
OpenGLInputLayout::~OpenGLInputLayout() {
|
2015-12-13 09:25:22 -08:00
|
|
|
if (id_) {
|
|
|
|
glDeleteVertexArrays(1, &id_);
|
|
|
|
}
|
|
|
|
}
|
2016-12-26 13:22:48 +01:00
|
|
|
|
|
|
|
void OpenGLInputLayout::Compile() {
|
|
|
|
int semMask = 0;
|
|
|
|
for (int i = 0; i < (int)desc.attributes.size(); i++) {
|
|
|
|
semMask |= 1 << desc.attributes[i].location;
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
2016-12-26 13:22:48 +01:00
|
|
|
semanticsMask_ = semMask;
|
2015-12-13 09:25:22 -08:00
|
|
|
|
2015-12-22 22:14:20 -08:00
|
|
|
if (gl_extensions.ARB_vertex_array_object && gl_extensions.IsCoreContext) {
|
2015-12-13 09:25:22 -08:00
|
|
|
glGenVertexArrays(1, &id_);
|
|
|
|
} else {
|
|
|
|
id_ = 0;
|
|
|
|
}
|
|
|
|
needsEnable_ = true;
|
|
|
|
lastBase_ = -1;
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
|
|
|
|
2016-12-26 13:22:48 +01:00
|
|
|
void OpenGLInputLayout::GLLost() {
|
2016-09-10 20:29:58 -07:00
|
|
|
id_ = 0;
|
|
|
|
}
|
|
|
|
|
2016-12-26 13:22:48 +01:00
|
|
|
void OpenGLInputLayout::GLRestore() {
|
2015-12-13 21:23:53 -08:00
|
|
|
Compile();
|
|
|
|
}
|
|
|
|
|
2016-12-25 21:21:56 +01:00
|
|
|
DepthStencilState *OpenGLContext::CreateDepthStencilState(const DepthStencilStateDesc &desc) {
|
|
|
|
OpenGLDepthStencilState *ds = new OpenGLDepthStencilState();
|
2016-12-25 21:10:46 +01:00
|
|
|
ds->depthTestEnabled = desc.depthTestEnabled;
|
|
|
|
ds->depthWriteEnabled = desc.depthWriteEnabled;
|
|
|
|
ds->depthComp = compToGL[(int)desc.depthCompare];
|
2016-12-26 23:11:31 +01:00
|
|
|
ds->stencilEnabled = desc.stencilEnabled;
|
|
|
|
ds->stencilCompareOp = compToGL[(int)desc.front.compareOp];
|
|
|
|
ds->stencilPass = stencilOpToGL[(int)desc.front.passOp];
|
|
|
|
ds->stencilFail = stencilOpToGL[(int)desc.front.failOp];
|
|
|
|
ds->stencilZFail = stencilOpToGL[(int)desc.front.depthFailOp];
|
|
|
|
ds->stencilWriteMask = desc.front.writeMask;
|
|
|
|
ds->stencilReference = desc.front.reference;
|
|
|
|
ds->stencilCompareMask = desc.front.compareMask;
|
2014-08-17 12:16:57 +02:00
|
|
|
return ds;
|
|
|
|
}
|
|
|
|
|
2016-12-25 21:21:56 +01:00
|
|
|
BlendState *OpenGLContext::CreateBlendState(const BlendStateDesc &desc) {
|
|
|
|
OpenGLBlendState *bs = new OpenGLBlendState();
|
2014-08-17 12:16:57 +02:00
|
|
|
bs->enabled = desc.enabled;
|
2016-12-25 21:10:46 +01:00
|
|
|
bs->eqCol = blendEqToGL[(int)desc.eqCol];
|
|
|
|
bs->srcCol = blendFactorToGL[(int)desc.srcCol];
|
|
|
|
bs->dstCol = blendFactorToGL[(int)desc.dstCol];
|
|
|
|
bs->eqAlpha = blendEqToGL[(int)desc.eqAlpha];
|
|
|
|
bs->srcAlpha = blendFactorToGL[(int)desc.srcAlpha];
|
|
|
|
bs->dstAlpha = blendFactorToGL[(int)desc.dstAlpha];
|
2015-01-04 17:59:58 +01:00
|
|
|
#ifndef USING_GLES2
|
2015-01-04 17:57:52 +01:00
|
|
|
bs->logicEnabled = desc.logicEnabled;
|
2016-12-25 18:52:05 +01:00
|
|
|
bs->logicOp = logicOpToGL[(int)desc.logicOp];
|
2015-01-04 17:59:58 +01:00
|
|
|
#endif
|
2016-12-26 18:32:52 +01:00
|
|
|
bs->colorMask = desc.colorMask;
|
2014-08-17 12:16:57 +02:00
|
|
|
return bs;
|
|
|
|
}
|
|
|
|
|
2016-12-25 21:21:56 +01:00
|
|
|
SamplerState *OpenGLContext::CreateSamplerState(const SamplerStateDesc &desc) {
|
|
|
|
OpenGLSamplerState *samps = new OpenGLSamplerState();
|
2017-02-19 11:22:23 +01:00
|
|
|
samps->wrapU = texWrapToGL[(int)desc.wrapU];
|
|
|
|
samps->wrapV = texWrapToGL[(int)desc.wrapV];
|
|
|
|
samps->wrapW = texWrapToGL[(int)desc.wrapW];
|
2016-12-25 21:21:56 +01:00
|
|
|
samps->magFilt = texFilterToGL[(int)desc.magFilter];
|
|
|
|
samps->minFilt = texFilterToGL[(int)desc.minFilter];
|
|
|
|
samps->mipMinFilt = texMipFilterToGL[(int)desc.minFilter][(int)desc.mipFilter];
|
2016-02-13 13:37:00 -08:00
|
|
|
return samps;
|
|
|
|
}
|
|
|
|
|
2016-12-25 22:24:14 +01:00
|
|
|
RasterState *OpenGLContext::CreateRasterState(const RasterStateDesc &desc) {
|
2016-12-25 21:21:56 +01:00
|
|
|
OpenGLRasterState *rs = new OpenGLRasterState();
|
2016-12-25 18:52:05 +01:00
|
|
|
if (desc.cull == CullMode::NONE) {
|
2016-12-23 23:46:11 +01:00
|
|
|
rs->cullEnable = GL_FALSE;
|
|
|
|
return rs;
|
|
|
|
}
|
|
|
|
rs->cullEnable = GL_TRUE;
|
2016-12-27 15:52:03 +01:00
|
|
|
switch (desc.frontFace) {
|
2016-12-25 18:52:05 +01:00
|
|
|
case Facing::CW:
|
2016-12-23 23:46:11 +01:00
|
|
|
rs->frontFace = GL_CW;
|
|
|
|
break;
|
2016-12-25 18:52:05 +01:00
|
|
|
case Facing::CCW:
|
2016-12-23 23:46:11 +01:00
|
|
|
rs->frontFace = GL_CCW;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
switch (desc.cull) {
|
2016-12-25 18:52:05 +01:00
|
|
|
case CullMode::FRONT:
|
2016-12-23 23:46:11 +01:00
|
|
|
rs->cullMode = GL_FRONT;
|
|
|
|
break;
|
2016-12-25 18:52:05 +01:00
|
|
|
case CullMode::BACK:
|
2016-12-23 23:46:11 +01:00
|
|
|
rs->cullMode = GL_BACK;
|
|
|
|
break;
|
2016-12-25 18:52:05 +01:00
|
|
|
case CullMode::FRONT_AND_BACK:
|
2016-12-23 23:46:11 +01:00
|
|
|
rs->cullMode = GL_FRONT_AND_BACK;
|
|
|
|
break;
|
2017-01-26 11:57:48 +01:00
|
|
|
case CullMode::NONE:
|
|
|
|
// Unsupported
|
|
|
|
break;
|
2016-12-23 23:46:11 +01:00
|
|
|
}
|
|
|
|
return rs;
|
|
|
|
}
|
|
|
|
|
2017-02-07 19:04:44 +01:00
|
|
|
class OpenGLBuffer : public Buffer, GfxResourceHolder {
|
|
|
|
public:
|
|
|
|
OpenGLBuffer(size_t size, uint32_t flags) {
|
|
|
|
glGenBuffers(1, &buffer_);
|
|
|
|
target_ = (flags & BufferUsageFlag::INDEXDATA) ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER;
|
|
|
|
usage_ = 0;
|
|
|
|
if (flags & BufferUsageFlag::DYNAMIC)
|
|
|
|
usage_ = GL_STREAM_DRAW;
|
|
|
|
else
|
|
|
|
usage_ = GL_STATIC_DRAW;
|
|
|
|
totalSize_ = size;
|
|
|
|
glBindBuffer(target_, buffer_);
|
|
|
|
glBufferData(target_, size, NULL, usage_);
|
2017-03-18 15:20:36 +01:00
|
|
|
register_gl_resource_holder(this, "drawcontext_buffer", 0);
|
2017-02-07 19:04:44 +01:00
|
|
|
}
|
|
|
|
~OpenGLBuffer() override {
|
|
|
|
unregister_gl_resource_holder(this);
|
|
|
|
glDeleteBuffers(1, &buffer_);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Bind(int offset) {
|
|
|
|
// TODO: Can't support offset using ES 2.0
|
|
|
|
glBindBuffer(target_, buffer_);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLLost() override {
|
|
|
|
buffer_ = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLRestore() override {
|
|
|
|
ILOG("Recreating vertex buffer after gl_restore");
|
|
|
|
totalSize_ = 0; // Will cause a new glBufferData call. Should genBuffers again though?
|
|
|
|
glGenBuffers(1, &buffer_);
|
|
|
|
}
|
|
|
|
|
|
|
|
GLuint buffer_;
|
|
|
|
GLuint target_;
|
|
|
|
GLuint usage_;
|
|
|
|
|
|
|
|
size_t totalSize_;
|
|
|
|
};
|
|
|
|
|
2016-12-25 21:21:56 +01:00
|
|
|
Buffer *OpenGLContext::CreateBuffer(size_t size, uint32_t usageFlags) {
|
|
|
|
return new OpenGLBuffer(size, usageFlags);
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
|
|
|
|
2017-02-07 19:41:58 +01:00
|
|
|
void OpenGLContext::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) {
|
2017-02-07 19:04:44 +01:00
|
|
|
OpenGLBuffer *buf = (OpenGLBuffer *)buffer;
|
|
|
|
|
|
|
|
buf->Bind(0);
|
|
|
|
if (size + offset > buf->totalSize_) {
|
|
|
|
Crash();
|
|
|
|
}
|
2017-02-07 19:41:58 +01:00
|
|
|
// if (flags & UPDATE_DISCARD) we could try to orphan the buffer using glBufferData.
|
2017-02-07 19:04:44 +01:00
|
|
|
glBufferSubData(buf->target_, offset, size, data);
|
2017-02-07 18:16:52 +01:00
|
|
|
}
|
|
|
|
|
2016-12-26 13:42:53 +01:00
|
|
|
Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
|
2016-12-25 23:03:20 +01:00
|
|
|
if (!desc.shaders.size()) {
|
2016-12-26 13:42:53 +01:00
|
|
|
ELOG("Pipeline requires at least one shader");
|
2014-08-23 10:36:42 -07:00
|
|
|
return NULL;
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
2016-12-26 13:42:53 +01:00
|
|
|
OpenGLPipeline *pipeline = new OpenGLPipeline();
|
2016-12-25 23:03:20 +01:00
|
|
|
for (auto iter : desc.shaders) {
|
|
|
|
iter->AddRef();
|
2016-12-26 13:42:53 +01:00
|
|
|
pipeline->shaders.push_back(static_cast<OpenGLShaderModule *>(iter));
|
2016-12-25 23:03:20 +01:00
|
|
|
}
|
2016-12-26 17:03:01 +01:00
|
|
|
if (pipeline->LinkShaders()) {
|
|
|
|
// Build the rest of the virtual pipeline object.
|
|
|
|
pipeline->prim = primToGL[(int)desc.prim];
|
|
|
|
pipeline->depthStencil = (OpenGLDepthStencilState *)desc.depthStencil;
|
|
|
|
pipeline->blend = (OpenGLBlendState *)desc.blend;
|
|
|
|
pipeline->raster = (OpenGLRasterState *)desc.raster;
|
|
|
|
pipeline->inputLayout = (OpenGLInputLayout *)desc.inputLayout;
|
|
|
|
pipeline->depthStencil->AddRef();
|
|
|
|
pipeline->blend->AddRef();
|
|
|
|
pipeline->raster->AddRef();
|
|
|
|
pipeline->inputLayout->AddRef();
|
2017-02-08 12:26:48 +01:00
|
|
|
if (desc.uniformDesc)
|
|
|
|
pipeline->dynamicUniforms = *desc.uniformDesc;
|
2016-12-26 13:42:53 +01:00
|
|
|
return pipeline;
|
2014-08-17 12:16:57 +02:00
|
|
|
} else {
|
2017-03-18 15:20:36 +01:00
|
|
|
ELOG("Failed to create pipeline - shaders failed to link");
|
2016-12-26 13:42:53 +01:00
|
|
|
delete pipeline;
|
2014-08-17 12:16:57 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-25 21:21:56 +01:00
|
|
|
void OpenGLContext::BindTextures(int start, int count, Texture **textures) {
|
2017-02-19 11:22:23 +01:00
|
|
|
maxTextures_ = std::max(maxTextures_, start + count);
|
2014-08-17 21:28:34 +02:00
|
|
|
for (int i = start; i < start + count; i++) {
|
2017-01-16 23:34:53 +07:00
|
|
|
OpenGLTexture *glTex = static_cast<OpenGLTexture *>(textures[i]);
|
2014-08-17 15:02:43 +02:00
|
|
|
glActiveTexture(GL_TEXTURE0 + i);
|
2017-02-08 15:37:40 +01:00
|
|
|
if (!glTex) {
|
2017-02-19 11:22:23 +01:00
|
|
|
boundTextures_[i] = 0;
|
2017-02-08 15:37:40 +01:00
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
continue;
|
|
|
|
}
|
2014-08-17 21:28:34 +02:00
|
|
|
glTex->Bind();
|
2017-02-19 11:22:23 +01:00
|
|
|
boundTextures_[i] = glTex;
|
2014-08-17 15:02:43 +02:00
|
|
|
}
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
}
|
|
|
|
|
2017-02-19 11:22:23 +01:00
|
|
|
void OpenGLContext::ApplySamplers() {
|
|
|
|
for (int i = 0; i < maxTextures_; i++) {
|
|
|
|
if ((int)boundSamplers_.size() > i && boundSamplers_[i]) {
|
|
|
|
const OpenGLSamplerState *samp = boundSamplers_[i];
|
|
|
|
const OpenGLTexture *tex = boundTextures_[i];
|
|
|
|
if (!tex)
|
|
|
|
continue;
|
|
|
|
if (tex->CanWrap()) {
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, samp->wrapU);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, samp->wrapV);
|
|
|
|
#ifndef USING_GLES2
|
|
|
|
if (tex->GetType() == TextureType::LINEAR3D)
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, samp->wrapW);
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
}
|
|
|
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, samp->magFilt);
|
|
|
|
if (tex->HasMips()) {
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, samp->mipMinFilt);
|
|
|
|
} else {
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, samp->minFilt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-08-17 15:02:43 +02:00
|
|
|
|
2016-12-27 17:38:26 +01:00
|
|
|
ShaderModule *OpenGLContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize) {
|
2016-12-25 22:24:14 +01:00
|
|
|
OpenGLShaderModule *shader = new OpenGLShaderModule(stage);
|
2016-12-27 17:38:26 +01:00
|
|
|
if (shader->Compile(language, data, dataSize)) {
|
2014-08-17 12:16:57 +02:00
|
|
|
return shader;
|
|
|
|
} else {
|
|
|
|
shader->Release();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-26 17:03:01 +01:00
|
|
|
bool OpenGLPipeline::LinkShaders() {
|
2014-09-06 13:49:01 +02:00
|
|
|
program_ = glCreateProgram();
|
2016-12-25 23:03:20 +01:00
|
|
|
for (auto iter : shaders) {
|
|
|
|
glAttachShader(program_, iter->GetShader());
|
|
|
|
}
|
2014-08-17 12:16:57 +02:00
|
|
|
|
|
|
|
// Bind all the common vertex data points. Mismatching ones will be ignored.
|
|
|
|
glBindAttribLocation(program_, SEM_POSITION, "Position");
|
|
|
|
glBindAttribLocation(program_, SEM_COLOR0, "Color0");
|
|
|
|
glBindAttribLocation(program_, SEM_TEXCOORD0, "TexCoord0");
|
|
|
|
glBindAttribLocation(program_, SEM_NORMAL, "Normal");
|
|
|
|
glBindAttribLocation(program_, SEM_TANGENT, "Tangent");
|
|
|
|
glBindAttribLocation(program_, SEM_BINORMAL, "Binormal");
|
|
|
|
glLinkProgram(program_);
|
|
|
|
|
|
|
|
GLint linkStatus = GL_FALSE;
|
|
|
|
glGetProgramiv(program_, GL_LINK_STATUS, &linkStatus);
|
|
|
|
if (linkStatus != GL_TRUE) {
|
|
|
|
GLint bufLength = 0;
|
|
|
|
glGetProgramiv(program_, GL_INFO_LOG_LENGTH, &bufLength);
|
|
|
|
if (bufLength) {
|
|
|
|
char* buf = new char[bufLength];
|
|
|
|
glGetProgramInfoLog(program_, bufLength, NULL, buf);
|
2014-08-23 10:31:23 +02:00
|
|
|
ELOG("Could not link program:\n %s", buf);
|
|
|
|
// We've thrown out the source at this point. Might want to do something about that.
|
|
|
|
#ifdef _WIN32
|
2014-08-17 12:16:57 +02:00
|
|
|
OutputDebugStringUTF8(buf);
|
|
|
|
#endif
|
2014-08-23 10:31:23 +02:00
|
|
|
delete[] buf;
|
2017-03-18 15:20:36 +01:00
|
|
|
} else {
|
|
|
|
ELOG("Could not link program with %d shaders for unknown reason:", (int)shaders.size());
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Auto-initialize samplers.
|
2014-08-23 00:34:55 -07:00
|
|
|
glUseProgram(program_);
|
2014-08-17 12:16:57 +02:00
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
char temp[256];
|
|
|
|
sprintf(temp, "Sampler%i", i);
|
|
|
|
int samplerLoc = GetUniformLoc(temp);
|
|
|
|
if (samplerLoc != -1) {
|
2014-08-23 00:34:55 -07:00
|
|
|
glUniform1i(samplerLoc, i);
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
|
|
|
}
|
2014-08-23 10:31:23 +02:00
|
|
|
|
2014-08-17 12:16:57 +02:00
|
|
|
// Here we could (using glGetAttribLocation) save a bitmask about which pieces of vertex data are used in the shader
|
|
|
|
// and then AND it with the vertex format bitmask later...
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-12-26 11:06:17 +01:00
|
|
|
int OpenGLPipeline::GetUniformLoc(const char *name) {
|
2017-02-08 12:26:48 +01:00
|
|
|
auto iter = uniformCache_.find(name);
|
2014-08-17 12:16:57 +02:00
|
|
|
int loc = -1;
|
2017-02-08 12:26:48 +01:00
|
|
|
if (iter != uniformCache_.end()) {
|
2014-08-17 12:16:57 +02:00
|
|
|
loc = iter->second.loc_;
|
|
|
|
} else {
|
|
|
|
loc = glGetUniformLocation(program_, name);
|
|
|
|
UniformInfo info;
|
|
|
|
info.loc_ = loc;
|
2017-02-08 12:26:48 +01:00
|
|
|
uniformCache_[name] = info;
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
|
|
|
return loc;
|
|
|
|
}
|
|
|
|
|
2016-12-26 17:03:01 +01:00
|
|
|
void OpenGLContext::BindPipeline(Pipeline *pipeline) {
|
|
|
|
curPipeline_ = (OpenGLPipeline *)pipeline;
|
|
|
|
curPipeline_->blend->Apply();
|
|
|
|
curPipeline_->depthStencil->Apply();
|
|
|
|
curPipeline_->raster->Apply();
|
2017-02-08 12:55:58 +01:00
|
|
|
glUseProgram(curPipeline_->program_);
|
2016-12-26 17:03:01 +01:00
|
|
|
}
|
|
|
|
|
2017-02-08 12:26:48 +01:00
|
|
|
void OpenGLContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) {
|
|
|
|
if (curPipeline_->dynamicUniforms.uniformBufferSize != size) {
|
|
|
|
Crash();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto &uniform : curPipeline_->dynamicUniforms.uniforms) {
|
|
|
|
GLuint loc = curPipeline_->GetUniformLoc(uniform.name);
|
|
|
|
if (loc == -1)
|
|
|
|
Crash();
|
|
|
|
const float *data = (const float *)((uint8_t *)ub + uniform.offset);
|
|
|
|
switch (uniform.type) {
|
|
|
|
case UniformType::FLOAT4:
|
|
|
|
glUniform1fv(loc, 4, data);
|
|
|
|
break;
|
|
|
|
case UniformType::MATRIX4X4:
|
|
|
|
glUniformMatrix4fv(loc, 1, false, data);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-17 23:14:47 +07:00
|
|
|
void OpenGLContext::Draw(int vertexCount, int offset) {
|
|
|
|
curVBuffers_[0]->Bind(curVBufferOffsets_[0]);
|
2016-12-26 17:03:01 +01:00
|
|
|
curPipeline_->inputLayout->Apply();
|
2017-02-19 11:22:23 +01:00
|
|
|
ApplySamplers();
|
2014-08-17 12:16:57 +02:00
|
|
|
|
2016-12-26 17:03:01 +01:00
|
|
|
glDrawArrays(curPipeline_->prim, offset, vertexCount);
|
2014-08-17 12:16:57 +02:00
|
|
|
|
2016-12-26 17:03:01 +01:00
|
|
|
curPipeline_->inputLayout->Unapply();
|
2017-05-24 12:06:02 +02:00
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
|
|
|
|
2017-01-17 23:14:47 +07:00
|
|
|
void OpenGLContext::DrawIndexed(int vertexCount, int offset) {
|
|
|
|
curVBuffers_[0]->Bind(curVBufferOffsets_[0]);
|
2016-12-26 17:03:01 +01:00
|
|
|
curPipeline_->inputLayout->Apply();
|
2017-02-19 11:22:23 +01:00
|
|
|
ApplySamplers();
|
2015-12-13 09:25:22 -08:00
|
|
|
// Note: ibuf binding is stored in the VAO, so call this after binding the fmt.
|
2017-01-17 23:14:47 +07:00
|
|
|
curIBuffer_->Bind(curIBufferOffset_);
|
2016-02-13 12:20:22 -08:00
|
|
|
|
2016-12-26 17:03:01 +01:00
|
|
|
glDrawElements(curPipeline_->prim, vertexCount, GL_UNSIGNED_INT, (const void *)(size_t)offset);
|
2014-08-17 12:16:57 +02:00
|
|
|
|
2016-12-26 17:03:01 +01:00
|
|
|
curPipeline_->inputLayout->Unapply();
|
2017-05-24 12:06:02 +02:00
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
2015-01-29 10:35:34 +01:00
|
|
|
}
|
|
|
|
|
2016-12-26 17:03:01 +01:00
|
|
|
void OpenGLContext::DrawUP(const void *vdata, int vertexCount) {
|
|
|
|
curPipeline_->inputLayout->Apply(vdata);
|
2017-02-19 11:22:23 +01:00
|
|
|
ApplySamplers();
|
2015-01-29 10:35:34 +01:00
|
|
|
|
2016-02-13 12:21:12 -08:00
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
2016-12-26 17:03:01 +01:00
|
|
|
glDrawArrays(curPipeline_->prim, 0, vertexCount);
|
2015-01-29 10:35:34 +01:00
|
|
|
|
2016-12-26 17:03:01 +01:00
|
|
|
curPipeline_->inputLayout->Unapply();
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
|
|
|
|
2016-12-25 21:21:56 +01:00
|
|
|
void OpenGLContext::Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) {
|
2014-08-17 12:16:57 +02:00
|
|
|
float col[4];
|
2015-10-10 16:41:19 +02:00
|
|
|
Uint8x4ToFloat4(col, colorval);
|
2014-08-17 12:16:57 +02:00
|
|
|
GLuint glMask = 0;
|
2017-02-15 11:06:59 +01:00
|
|
|
if (mask & FBChannel::FB_COLOR_BIT) {
|
2014-08-17 12:16:57 +02:00
|
|
|
glClearColor(col[0], col[1], col[2], col[3]);
|
|
|
|
glMask |= GL_COLOR_BUFFER_BIT;
|
|
|
|
}
|
2017-02-15 11:06:59 +01:00
|
|
|
if (mask & FBChannel::FB_DEPTH_BIT) {
|
2015-09-06 12:21:15 +02:00
|
|
|
#if defined(USING_GLES2)
|
2014-08-23 00:34:55 -07:00
|
|
|
glClearDepthf(depthVal);
|
|
|
|
#else
|
2014-08-17 12:16:57 +02:00
|
|
|
glClearDepth(depthVal);
|
2014-08-23 00:34:55 -07:00
|
|
|
#endif
|
2014-08-17 12:16:57 +02:00
|
|
|
glMask |= GL_DEPTH_BUFFER_BIT;
|
|
|
|
}
|
2017-02-15 11:06:59 +01:00
|
|
|
if (mask & FBChannel::FB_STENCIL_BIT) {
|
2014-08-17 12:16:57 +02:00
|
|
|
glClearStencil(stencilVal);
|
|
|
|
glMask |= GL_STENCIL_BUFFER_BIT;
|
|
|
|
}
|
|
|
|
glClear(glMask);
|
|
|
|
}
|
|
|
|
|
2016-12-25 21:01:57 +01:00
|
|
|
DrawContext *T3DCreateGLContext() {
|
2016-12-25 21:21:56 +01:00
|
|
|
return new OpenGLContext();
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
|
|
|
|
2016-12-26 13:22:48 +01:00
|
|
|
void OpenGLInputLayout::Apply(const void *base) {
|
2015-12-13 09:25:22 -08:00
|
|
|
if (id_ != 0) {
|
|
|
|
glBindVertexArray(id_);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (needsEnable_ || id_ == 0) {
|
|
|
|
for (int i = 0; i < SEM_MAX; i++) {
|
|
|
|
if (semanticsMask_ & (1 << i)) {
|
|
|
|
glEnableVertexAttribArray(i);
|
|
|
|
}
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
2015-12-22 22:13:50 -08:00
|
|
|
if (id_ != 0) {
|
|
|
|
needsEnable_ = false;
|
|
|
|
}
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
2015-12-13 09:25:22 -08:00
|
|
|
|
2015-01-29 10:35:34 +01:00
|
|
|
intptr_t b = (intptr_t)base;
|
2015-12-13 09:25:22 -08:00
|
|
|
if (b != lastBase_) {
|
2016-12-26 13:22:48 +01:00
|
|
|
for (size_t i = 0; i < desc.attributes.size(); i++) {
|
2016-12-26 13:42:53 +01:00
|
|
|
GLsizei stride = (GLsizei)desc.bindings[desc.attributes[i].binding].stride;
|
2016-12-26 13:22:48 +01:00
|
|
|
switch (desc.attributes[i].format) {
|
2016-12-25 21:21:56 +01:00
|
|
|
case DataFormat::R32G32_FLOAT:
|
2016-12-26 13:22:48 +01:00
|
|
|
glVertexAttribPointer(desc.attributes[i].location, 2, GL_FLOAT, GL_FALSE, stride, (void *)(b + (intptr_t)desc.attributes[i].offset));
|
2015-12-13 09:25:22 -08:00
|
|
|
break;
|
2016-12-25 21:21:56 +01:00
|
|
|
case DataFormat::R32G32B32_FLOAT:
|
2016-12-26 13:22:48 +01:00
|
|
|
glVertexAttribPointer(desc.attributes[i].location, 3, GL_FLOAT, GL_FALSE, stride, (void *)(b + (intptr_t)desc.attributes[i].offset));
|
2015-12-13 09:25:22 -08:00
|
|
|
break;
|
2016-12-25 21:21:56 +01:00
|
|
|
case DataFormat::R32G32B32A32_FLOAT:
|
2016-12-26 13:22:48 +01:00
|
|
|
glVertexAttribPointer(desc.attributes[i].location, 4, GL_FLOAT, GL_FALSE, stride, (void *)(b + (intptr_t)desc.attributes[i].offset));
|
2015-12-13 09:25:22 -08:00
|
|
|
break;
|
2016-12-25 21:21:56 +01:00
|
|
|
case DataFormat::R8G8B8A8_UNORM:
|
2016-12-26 13:22:48 +01:00
|
|
|
glVertexAttribPointer(desc.attributes[i].location, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void *)(b + (intptr_t)desc.attributes[i].offset));
|
2015-12-13 09:25:22 -08:00
|
|
|
break;
|
2016-12-25 22:11:14 +01:00
|
|
|
case DataFormat::UNDEFINED:
|
2016-12-23 09:58:15 +01:00
|
|
|
default:
|
|
|
|
ELOG("Thin3DGLVertexFormat: Invalid or unknown component type applied.");
|
|
|
|
break;
|
2015-12-13 09:25:22 -08:00
|
|
|
}
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
2015-12-22 22:13:50 -08:00
|
|
|
if (id_ != 0) {
|
|
|
|
lastBase_ = b;
|
|
|
|
}
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-26 13:22:48 +01:00
|
|
|
void OpenGLInputLayout::Unapply() {
|
2015-12-13 09:25:22 -08:00
|
|
|
if (id_ == 0) {
|
2016-12-25 18:52:05 +01:00
|
|
|
for (int i = 0; i < (int)SEM_MAX; i++) {
|
2015-12-13 09:25:22 -08:00
|
|
|
if (semanticsMask_ & (1 << i)) {
|
|
|
|
glDisableVertexAttribArray(i);
|
|
|
|
}
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
2015-12-13 09:25:22 -08:00
|
|
|
} else {
|
|
|
|
glBindVertexArray(0);
|
2014-08-17 12:16:57 +02:00
|
|
|
}
|
2014-11-23 11:01:51 -08:00
|
|
|
}
|
2016-12-25 18:18:19 +01:00
|
|
|
|
2017-03-18 13:26:18 +01:00
|
|
|
class OpenGLFramebuffer : public Framebuffer, public GfxResourceHolder {
|
2017-02-04 18:46:12 +01:00
|
|
|
public:
|
2017-03-18 13:26:18 +01:00
|
|
|
OpenGLFramebuffer() {
|
2017-03-18 15:20:36 +01:00
|
|
|
register_gl_resource_holder(this, "framebuffer", 0);
|
2017-03-18 13:26:18 +01:00
|
|
|
}
|
2017-02-04 18:46:12 +01:00
|
|
|
~OpenGLFramebuffer();
|
2017-03-18 13:26:18 +01:00
|
|
|
|
|
|
|
void GLLost() override {
|
|
|
|
handle = 0;
|
|
|
|
color_texture = 0;
|
|
|
|
z_stencil_buffer = 0;
|
|
|
|
z_buffer = 0;
|
|
|
|
stencil_buffer = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLRestore() override {
|
|
|
|
ELOG("Restoring framebuffers not yet implemented");
|
|
|
|
}
|
|
|
|
|
2017-02-15 23:56:38 +01:00
|
|
|
GLuint handle = 0;
|
|
|
|
GLuint color_texture = 0;
|
|
|
|
GLuint z_stencil_buffer = 0; // Either this is set, or the two below.
|
|
|
|
GLuint z_buffer = 0;
|
|
|
|
GLuint stencil_buffer = 0;
|
2017-02-04 18:46:12 +01:00
|
|
|
|
|
|
|
int width;
|
|
|
|
int height;
|
|
|
|
FBColorDepth colorDepth;
|
|
|
|
};
|
|
|
|
|
|
|
|
// On PC, we always use GL_DEPTH24_STENCIL8.
|
|
|
|
// On Android, we try to use what's available.
|
|
|
|
|
|
|
|
#ifndef USING_GLES2
|
|
|
|
OpenGLFramebuffer *OpenGLContext::fbo_ext_create(const FramebufferDesc &desc) {
|
|
|
|
OpenGLFramebuffer *fbo = new OpenGLFramebuffer();
|
|
|
|
fbo->width = desc.width;
|
|
|
|
fbo->height = desc.height;
|
|
|
|
fbo->colorDepth = desc.colorDepth;
|
|
|
|
|
|
|
|
// Color texture is same everywhere
|
|
|
|
glGenFramebuffersEXT(1, &fbo->handle);
|
|
|
|
glGenTextures(1, &fbo->color_texture);
|
|
|
|
|
|
|
|
// Create the surfaces.
|
|
|
|
glBindTexture(GL_TEXTURE_2D, fbo->color_texture);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
|
|
|
|
// TODO: We could opt to only create 16-bit render targets on slow devices. For later.
|
|
|
|
switch (fbo->colorDepth) {
|
|
|
|
case FBO_8888:
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
|
|
|
break;
|
|
|
|
case FBO_4444:
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, NULL);
|
|
|
|
break;
|
|
|
|
case FBO_5551:
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, NULL);
|
|
|
|
break;
|
|
|
|
case FBO_565:
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, fbo->width, fbo->height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
|
|
|
|
fbo->stencil_buffer = 0;
|
|
|
|
fbo->z_buffer = 0;
|
|
|
|
// 24-bit Z, 8-bit stencil
|
|
|
|
glGenRenderbuffersEXT(1, &fbo->z_stencil_buffer);
|
|
|
|
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo->z_stencil_buffer);
|
|
|
|
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_EXT, fbo->width, fbo->height);
|
|
|
|
//glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8, width, height);
|
|
|
|
|
|
|
|
// Bind it all together
|
|
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->handle);
|
|
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, fbo->color_texture, 0);
|
|
|
|
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo->z_stencil_buffer);
|
|
|
|
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo->z_stencil_buffer);
|
|
|
|
|
|
|
|
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
|
|
|
|
switch (status) {
|
|
|
|
case GL_FRAMEBUFFER_COMPLETE_EXT:
|
|
|
|
// ILOG("Framebuffer verified complete.");
|
|
|
|
break;
|
|
|
|
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
|
|
|
|
ELOG("GL_FRAMEBUFFER_UNSUPPORTED");
|
|
|
|
break;
|
|
|
|
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
|
|
|
|
ELOG("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT ");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
FLOG("Other framebuffer error: %i", status);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Unbind state we don't need
|
|
|
|
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
|
|
|
|
currentDrawHandle_ = fbo->handle;
|
|
|
|
currentReadHandle_ = fbo->handle;
|
|
|
|
return fbo;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-02-06 11:26:24 +01:00
|
|
|
Framebuffer *OpenGLContext::CreateFramebuffer(const FramebufferDesc &desc) {
|
2017-02-04 18:46:12 +01:00
|
|
|
CheckGLExtensions();
|
|
|
|
|
|
|
|
#ifndef USING_GLES2
|
|
|
|
if (!gl_extensions.ARB_framebuffer_object && gl_extensions.EXT_framebuffer_object) {
|
|
|
|
return fbo_ext_create(desc);
|
|
|
|
} else if (!gl_extensions.ARB_framebuffer_object) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
// If GLES2, we have basic FBO support and can just proceed.
|
|
|
|
#endif
|
2017-03-03 14:15:27 +01:00
|
|
|
CHECK_GL_ERROR_IF_DEBUG();
|
2017-02-04 18:46:12 +01:00
|
|
|
|
|
|
|
OpenGLFramebuffer *fbo = new OpenGLFramebuffer();
|
|
|
|
fbo->width = desc.width;
|
|
|
|
fbo->height = desc.height;
|
|
|
|
fbo->colorDepth = desc.colorDepth;
|
|
|
|
|
|
|
|
// Color texture is same everywhere
|
|
|
|
glGenFramebuffers(1, &fbo->handle);
|
|
|
|
glGenTextures(1, &fbo->color_texture);
|
|
|
|
|
|
|
|
// Create the surfaces.
|
|
|
|
glBindTexture(GL_TEXTURE_2D, fbo->color_texture);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
|
|
|
|
// TODO: We could opt to only create 16-bit render targets on slow devices. For later.
|
|
|
|
switch (fbo->colorDepth) {
|
|
|
|
case FBO_8888:
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
|
|
|
break;
|
|
|
|
case FBO_4444:
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, NULL);
|
|
|
|
break;
|
|
|
|
case FBO_5551:
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, NULL);
|
|
|
|
break;
|
|
|
|
case FBO_565:
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, fbo->width, fbo->height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
|
|
|
|
if (gl_extensions.IsGLES) {
|
|
|
|
if (gl_extensions.OES_packed_depth_stencil) {
|
|
|
|
ILOG("Creating %i x %i FBO using DEPTH24_STENCIL8", fbo->width, fbo->height);
|
|
|
|
// Standard method
|
|
|
|
fbo->stencil_buffer = 0;
|
|
|
|
fbo->z_buffer = 0;
|
|
|
|
// 24-bit Z, 8-bit stencil combined
|
|
|
|
glGenRenderbuffers(1, &fbo->z_stencil_buffer);
|
|
|
|
glBindRenderbuffer(GL_RENDERBUFFER, fbo->z_stencil_buffer);
|
|
|
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, fbo->width, fbo->height);
|
|
|
|
|
|
|
|
// Bind it all together
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle);
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0);
|
|
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer);
|
|
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer);
|
|
|
|
} else {
|
|
|
|
ILOG("Creating %i x %i FBO using separate stencil", fbo->width, fbo->height);
|
|
|
|
// TEGRA
|
|
|
|
fbo->z_stencil_buffer = 0;
|
|
|
|
// 16/24-bit Z, separate 8-bit stencil
|
|
|
|
glGenRenderbuffers(1, &fbo->z_buffer);
|
|
|
|
glBindRenderbuffer(GL_RENDERBUFFER, fbo->z_buffer);
|
|
|
|
// Don't forget to make sure fbo_standard_z_depth() matches.
|
|
|
|
glRenderbufferStorage(GL_RENDERBUFFER, gl_extensions.OES_depth24 ? GL_DEPTH_COMPONENT24 : GL_DEPTH_COMPONENT16, fbo->width, fbo->height);
|
|
|
|
|
|
|
|
// 8-bit stencil buffer
|
|
|
|
glGenRenderbuffers(1, &fbo->stencil_buffer);
|
|
|
|
glBindRenderbuffer(GL_RENDERBUFFER, fbo->stencil_buffer);
|
|
|
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, fbo->width, fbo->height);
|
|
|
|
|
|
|
|
// Bind it all together
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle);
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0);
|
|
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_buffer);
|
|
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->stencil_buffer);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fbo->stencil_buffer = 0;
|
|
|
|
fbo->z_buffer = 0;
|
|
|
|
// 24-bit Z, 8-bit stencil
|
|
|
|
glGenRenderbuffers(1, &fbo->z_stencil_buffer);
|
|
|
|
glBindRenderbuffer(GL_RENDERBUFFER, fbo->z_stencil_buffer);
|
|
|
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, fbo->width, fbo->height);
|
|
|
|
|
|
|
|
// Bind it all together
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle);
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0);
|
|
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer);
|
|
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
|
|
|
switch (status) {
|
|
|
|
case GL_FRAMEBUFFER_COMPLETE:
|
|
|
|
// ILOG("Framebuffer verified complete.");
|
|
|
|
break;
|
|
|
|
case GL_FRAMEBUFFER_UNSUPPORTED:
|
|
|
|
ELOG("GL_FRAMEBUFFER_UNSUPPORTED");
|
|
|
|
break;
|
|
|
|
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
|
|
|
|
ELOG("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT ");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
FLOG("Other framebuffer error: %i", status);
|
|
|
|
break;
|
|
|
|
}
|
2017-05-16 16:00:34 +02:00
|
|
|
|
2017-02-04 18:46:12 +01:00
|
|
|
// Unbind state we don't need
|
|
|
|
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
2017-03-03 14:15:27 +01:00
|
|
|
CHECK_GL_ERROR_IF_DEBUG();
|
2017-02-04 18:46:12 +01:00
|
|
|
|
|
|
|
currentDrawHandle_ = fbo->handle;
|
|
|
|
currentReadHandle_ = fbo->handle;
|
|
|
|
return fbo;
|
|
|
|
}
|
|
|
|
|
|
|
|
GLenum OpenGLContext::fbo_get_fb_target(bool read, GLuint **cached) {
|
|
|
|
bool supportsBlit = gl_extensions.ARB_framebuffer_object;
|
|
|
|
if (gl_extensions.IsGLES) {
|
|
|
|
supportsBlit = (gl_extensions.GLES3 || gl_extensions.NV_framebuffer_blit);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note: GL_FRAMEBUFFER_EXT and GL_FRAMEBUFFER have the same value, same with _NV.
|
|
|
|
if (supportsBlit) {
|
|
|
|
if (read) {
|
|
|
|
*cached = ¤tReadHandle_;
|
|
|
|
return GL_READ_FRAMEBUFFER;
|
|
|
|
} else {
|
|
|
|
*cached = ¤tDrawHandle_;
|
|
|
|
return GL_DRAW_FRAMEBUFFER;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
*cached = ¤tDrawHandle_;
|
|
|
|
return GL_FRAMEBUFFER;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLContext::fbo_bind_fb_target(bool read, GLuint name) {
|
|
|
|
GLuint *cached;
|
|
|
|
GLenum target = fbo_get_fb_target(read, &cached);
|
|
|
|
if (*cached != name) {
|
|
|
|
if (gl_extensions.ARB_framebuffer_object || gl_extensions.IsGLES) {
|
|
|
|
glBindFramebuffer(target, name);
|
|
|
|
} else {
|
|
|
|
#ifndef USING_GLES2
|
|
|
|
glBindFramebufferEXT(target, name);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
*cached = name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGLContext::fbo_unbind() {
|
|
|
|
#ifndef USING_GLES2
|
|
|
|
if (gl_extensions.ARB_framebuffer_object || gl_extensions.IsGLES) {
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
} else if (gl_extensions.EXT_framebuffer_object) {
|
|
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef IOS
|
|
|
|
bindDefaultFBO();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
currentDrawHandle_ = 0;
|
|
|
|
currentReadHandle_ = 0;
|
|
|
|
}
|
|
|
|
|
2017-05-16 16:00:34 +02:00
|
|
|
void OpenGLContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) {
|
2017-03-03 14:15:27 +01:00
|
|
|
CHECK_GL_ERROR_IF_DEBUG();
|
2017-05-16 13:53:57 +02:00
|
|
|
if (fbo) {
|
|
|
|
OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo;
|
|
|
|
// Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing
|
|
|
|
// in ES 2.0 that actually separate them anyway of course, so doesn't matter.
|
|
|
|
fbo_bind_fb_target(false, fb->handle);
|
|
|
|
// Always restore viewport after render target binding. Works around driver bugs.
|
|
|
|
glstate.viewport.restore();
|
|
|
|
} else {
|
|
|
|
fbo_unbind();
|
|
|
|
}
|
2017-05-16 16:00:34 +02:00
|
|
|
int clearFlags = 0;
|
|
|
|
if (rp.color == RPAction::CLEAR) {
|
|
|
|
float fc[4]{};
|
|
|
|
if (rp.clearColor) {
|
|
|
|
Uint8x4ToFloat4(fc, rp.clearColor);
|
|
|
|
}
|
|
|
|
glClearColor(fc[0], fc[1], fc[2], fc[3]);
|
|
|
|
clearFlags |= GL_COLOR_BUFFER_BIT;
|
|
|
|
glstate.colorMask.force(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
|
|
}
|
|
|
|
if (rp.depth == RPAction::CLEAR) {
|
2017-06-01 08:59:45 +02:00
|
|
|
#ifdef USING_GLES2
|
2017-05-22 14:48:20 +02:00
|
|
|
glClearDepthf(rp.clearDepth);
|
2017-06-01 08:59:45 +02:00
|
|
|
#else
|
|
|
|
glClearDepth(rp.clearDepth);
|
|
|
|
#endif
|
2017-05-16 16:00:34 +02:00
|
|
|
glClearStencil(rp.clearStencil);
|
|
|
|
clearFlags |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
|
|
|
|
glstate.depthWrite.force(GL_TRUE);
|
|
|
|
glstate.stencilFunc.force(GL_ALWAYS, 0, 0);
|
|
|
|
glstate.stencilMask.force(0xFF);
|
|
|
|
}
|
|
|
|
if (clearFlags) {
|
|
|
|
glstate.scissorTest.force(false);
|
|
|
|
glClear(clearFlags);
|
|
|
|
glstate.scissorTest.restore();
|
|
|
|
}
|
|
|
|
if (rp.color == RPAction::CLEAR) {
|
|
|
|
glstate.colorMask.restore();
|
|
|
|
}
|
|
|
|
if (rp.depth == RPAction::CLEAR) {
|
|
|
|
glstate.depthWrite.restore();
|
|
|
|
glstate.stencilFunc.restore();
|
|
|
|
glstate.stencilMask.restore();
|
|
|
|
}
|
2017-03-03 14:15:27 +01:00
|
|
|
CHECK_GL_ERROR_IF_DEBUG();
|
2017-02-04 18:46:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// For GL_EXT_FRAMEBUFFER_BLIT and similar.
|
2017-02-06 11:26:24 +01:00
|
|
|
void OpenGLContext::BindFramebufferForRead(Framebuffer *fbo) {
|
2017-02-04 18:46:12 +01:00
|
|
|
OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo;
|
|
|
|
fbo_bind_fb_target(true, fb->handle);
|
2017-03-03 14:15:27 +01:00
|
|
|
CHECK_GL_ERROR_IF_DEBUG();
|
2017-02-04 18:46:12 +01:00
|
|
|
}
|
|
|
|
|
2017-02-12 11:20:55 +01:00
|
|
|
void OpenGLContext::CopyFramebufferImage(Framebuffer *fbsrc, int srcLevel, int srcX, int srcY, int srcZ, Framebuffer *fbdst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) {
|
2017-02-04 18:46:12 +01:00
|
|
|
OpenGLFramebuffer *src = (OpenGLFramebuffer *)fbsrc;
|
|
|
|
OpenGLFramebuffer *dst = (OpenGLFramebuffer *)fbdst;
|
2017-02-12 11:20:55 +01:00
|
|
|
GLuint srcTex = 0;
|
|
|
|
GLuint dstTex = 0;
|
2017-02-21 10:44:52 +01:00
|
|
|
GLuint target = GL_TEXTURE_2D;
|
2017-02-12 11:20:55 +01:00
|
|
|
switch (channelBits) {
|
|
|
|
case FB_COLOR_BIT:
|
|
|
|
srcTex = src->color_texture;
|
|
|
|
dstTex = dst->color_texture;
|
|
|
|
break;
|
|
|
|
case FB_DEPTH_BIT:
|
2017-02-21 10:44:52 +01:00
|
|
|
target = GL_RENDERBUFFER;
|
2017-02-12 11:20:55 +01:00
|
|
|
srcTex = src->z_buffer ? src->z_buffer : src->z_stencil_buffer;
|
|
|
|
dstTex = dst->z_buffer ? dst->z_buffer : dst->z_stencil_buffer;
|
|
|
|
break;
|
|
|
|
}
|
2017-02-04 18:46:12 +01:00
|
|
|
#if defined(USING_GLES2)
|
|
|
|
#ifndef IOS
|
|
|
|
glCopyImageSubDataOES(
|
2017-02-21 10:44:52 +01:00
|
|
|
srcTex, target, srcLevel, srcX, srcY, srcZ,
|
|
|
|
dstTex, target, dstLevel, dstX, dstY, dstZ,
|
2017-02-04 18:46:12 +01:00
|
|
|
width, height, depth);
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
if (gl_extensions.ARB_copy_image) {
|
|
|
|
glCopyImageSubData(
|
2017-02-21 10:44:52 +01:00
|
|
|
srcTex, target, srcLevel, srcX, srcY, srcZ,
|
|
|
|
dstTex, target, dstLevel, dstX, dstY, dstZ,
|
2017-02-04 18:46:12 +01:00
|
|
|
width, height, depth);
|
|
|
|
} else if (gl_extensions.NV_copy_image) {
|
|
|
|
// Older, pre GL 4.x NVIDIA cards.
|
|
|
|
glCopyImageSubDataNV(
|
2017-02-21 10:44:52 +01:00
|
|
|
srcTex, target, srcLevel, srcX, srcY, srcZ,
|
|
|
|
dstTex, target, dstLevel, dstX, dstY, dstZ,
|
2017-02-04 18:46:12 +01:00
|
|
|
width, height, depth);
|
|
|
|
}
|
|
|
|
#endif
|
2017-03-03 14:15:27 +01:00
|
|
|
CHECK_GL_ERROR_IF_DEBUG();
|
2017-02-04 18:46:12 +01:00
|
|
|
}
|
|
|
|
|
2017-02-06 11:26:24 +01:00
|
|
|
bool OpenGLContext::BlitFramebuffer(Framebuffer *fbsrc, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *fbdst, int dstX1, int dstY1, int dstX2, int dstY2, int channels, FBBlitFilter linearFilter) {
|
2017-02-04 18:46:12 +01:00
|
|
|
OpenGLFramebuffer *src = (OpenGLFramebuffer *)fbsrc;
|
|
|
|
OpenGLFramebuffer *dst = (OpenGLFramebuffer *)fbdst;
|
|
|
|
GLuint bits = 0;
|
|
|
|
if (channels & FB_COLOR_BIT)
|
|
|
|
bits |= GL_COLOR_BUFFER_BIT;
|
|
|
|
if (channels & FB_DEPTH_BIT)
|
|
|
|
bits |= GL_DEPTH_BUFFER_BIT;
|
|
|
|
if (channels & FB_STENCIL_BIT)
|
|
|
|
bits |= GL_STENCIL_BUFFER_BIT;
|
2017-05-16 14:27:34 +02:00
|
|
|
// Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing
|
|
|
|
// in ES 2.0 that actually separate them anyway of course, so doesn't matter.
|
|
|
|
fbo_bind_fb_target(false, dst->handle);
|
|
|
|
fbo_bind_fb_target(true, src->handle);
|
2017-02-04 18:46:12 +01:00
|
|
|
if (gl_extensions.GLES3 || gl_extensions.ARB_framebuffer_object) {
|
|
|
|
glBlitFramebuffer(srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2, bits, linearFilter == FB_BLIT_LINEAR ? GL_LINEAR : GL_NEAREST);
|
2017-03-03 14:15:27 +01:00
|
|
|
CHECK_GL_ERROR_IF_DEBUG();
|
2017-02-04 18:46:12 +01:00
|
|
|
#if defined(USING_GLES2) && defined(__ANDROID__) // We only support this extension on Android, it's not even available on PC.
|
|
|
|
return true;
|
|
|
|
} else if (gl_extensions.NV_framebuffer_blit) {
|
|
|
|
glBlitFramebufferNV(srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2, bits, linearFilter == FB_BLIT_LINEAR ? GL_LINEAR : GL_NEAREST);
|
2017-03-03 14:15:27 +01:00
|
|
|
CHECK_GL_ERROR_IF_DEBUG();
|
2017-02-04 18:46:12 +01:00
|
|
|
#endif // defined(USING_GLES2) && defined(__ANDROID__)
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-06 11:26:24 +01:00
|
|
|
uintptr_t OpenGLContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBits, int attachment) {
|
2017-03-03 14:15:27 +01:00
|
|
|
OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo;
|
|
|
|
switch (channelBits) {
|
|
|
|
case FB_COLOR_BIT: return (uintptr_t)fb->color_texture;
|
|
|
|
case FB_DEPTH_BIT: return (uintptr_t)(fb->z_buffer ? fb->z_buffer : fb->z_stencil_buffer);
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
2017-02-04 18:46:12 +01:00
|
|
|
}
|
|
|
|
|
2017-02-06 11:26:24 +01:00
|
|
|
void OpenGLContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int color) {
|
2017-02-04 18:46:12 +01:00
|
|
|
OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo;
|
2017-02-15 23:56:38 +01:00
|
|
|
if (!fb)
|
|
|
|
return;
|
|
|
|
if (binding != 0)
|
|
|
|
glActiveTexture(GL_TEXTURE0 + binding);
|
2017-02-04 18:46:12 +01:00
|
|
|
switch (channelBit) {
|
2017-02-15 23:56:38 +01:00
|
|
|
case FB_DEPTH_BIT:
|
|
|
|
glBindTexture(GL_TEXTURE_2D, fb->z_buffer ? fb->z_buffer : fb->z_stencil_buffer);
|
2017-02-04 18:46:12 +01:00
|
|
|
case FB_COLOR_BIT:
|
|
|
|
default:
|
2017-02-15 23:56:38 +01:00
|
|
|
glBindTexture(GL_TEXTURE_2D, fb->color_texture);
|
2017-02-04 18:46:12 +01:00
|
|
|
break;
|
|
|
|
}
|
2017-02-15 23:56:38 +01:00
|
|
|
glActiveTexture(GL_TEXTURE0);
|
2017-02-04 18:46:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
OpenGLFramebuffer::~OpenGLFramebuffer() {
|
2017-03-18 13:26:18 +01:00
|
|
|
unregister_gl_resource_holder(this);
|
2017-03-03 14:15:27 +01:00
|
|
|
CHECK_GL_ERROR_IF_DEBUG();
|
2017-02-04 18:46:12 +01:00
|
|
|
if (gl_extensions.ARB_framebuffer_object || gl_extensions.IsGLES) {
|
2017-03-18 13:26:18 +01:00
|
|
|
if (handle) {
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, handle);
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
|
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
2017-02-15 23:56:38 +01:00
|
|
|
glDeleteFramebuffers(1, &handle);
|
2017-03-18 13:26:18 +01:00
|
|
|
}
|
2017-02-15 23:56:38 +01:00
|
|
|
if (z_stencil_buffer)
|
|
|
|
glDeleteRenderbuffers(1, &z_stencil_buffer);
|
|
|
|
if (z_buffer)
|
|
|
|
glDeleteRenderbuffers(1, &z_buffer);
|
|
|
|
if (stencil_buffer)
|
|
|
|
glDeleteRenderbuffers(1, &stencil_buffer);
|
2017-02-04 18:46:12 +01:00
|
|
|
} else if (gl_extensions.EXT_framebuffer_object) {
|
|
|
|
#ifndef USING_GLES2
|
2017-03-18 13:26:18 +01:00
|
|
|
if (handle) {
|
|
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, handle);
|
|
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
|
|
|
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER_EXT, 0);
|
|
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
2017-02-15 23:56:38 +01:00
|
|
|
glDeleteFramebuffersEXT(1, &handle);
|
2017-03-18 13:26:18 +01:00
|
|
|
}
|
2017-02-15 23:56:38 +01:00
|
|
|
if (z_stencil_buffer)
|
|
|
|
glDeleteRenderbuffers(1, &z_stencil_buffer);
|
|
|
|
if (z_buffer)
|
|
|
|
glDeleteRenderbuffers(1, &z_buffer);
|
|
|
|
if (stencil_buffer)
|
|
|
|
glDeleteRenderbuffers(1, &stencil_buffer);
|
2017-02-04 18:46:12 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
glDeleteTextures(1, &color_texture);
|
|
|
|
}
|
|
|
|
|
2017-02-06 11:26:24 +01:00
|
|
|
void OpenGLContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) {
|
2017-02-04 18:46:12 +01:00
|
|
|
OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo;
|
|
|
|
*w = fb->width;
|
|
|
|
*h = fb->height;
|
|
|
|
}
|
|
|
|
|
2017-01-19 12:16:36 +07:00
|
|
|
uint32_t OpenGLContext::GetDataFormatSupport(DataFormat fmt) const {
|
|
|
|
switch (fmt) {
|
|
|
|
case DataFormat::B8G8R8A8_UNORM:
|
2017-03-11 14:43:42 +01:00
|
|
|
return FMT_RENDERTARGET | FMT_TEXTURE | FMT_AUTOGEN_MIPS;
|
2017-01-21 13:11:03 +01:00
|
|
|
case DataFormat::B4G4R4A4_UNORM_PACK16:
|
2017-03-11 14:43:42 +01:00
|
|
|
return FMT_RENDERTARGET | FMT_TEXTURE | FMT_AUTOGEN_MIPS; // native support
|
2017-02-22 16:23:04 +01:00
|
|
|
case DataFormat::A4R4G4B4_UNORM_PACK16:
|
2017-02-19 11:09:42 +01:00
|
|
|
#ifndef USING_GLES2
|
|
|
|
// Can support this if _REV formats are supported.
|
|
|
|
return FMT_TEXTURE;
|
|
|
|
#endif
|
|
|
|
return 0;
|
2017-01-19 12:16:36 +07:00
|
|
|
|
|
|
|
case DataFormat::R8G8B8A8_UNORM:
|
2017-03-11 14:43:42 +01:00
|
|
|
return FMT_RENDERTARGET | FMT_TEXTURE | FMT_INPUTLAYOUT | FMT_AUTOGEN_MIPS;
|
2017-01-19 12:16:36 +07:00
|
|
|
|
|
|
|
case DataFormat::R32_FLOAT:
|
|
|
|
case DataFormat::R32G32_FLOAT:
|
|
|
|
case DataFormat::R32G32B32_FLOAT:
|
|
|
|
case DataFormat::R32G32B32A32_FLOAT:
|
|
|
|
return FMT_INPUTLAYOUT;
|
|
|
|
|
|
|
|
case DataFormat::R8_UNORM:
|
|
|
|
return 0;
|
|
|
|
case DataFormat::BC1_RGBA_UNORM_BLOCK:
|
|
|
|
case DataFormat::BC2_UNORM_BLOCK:
|
|
|
|
case DataFormat::BC3_UNORM_BLOCK:
|
|
|
|
return FMT_TEXTURE;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-26 11:57:48 +01:00
|
|
|
} // namespace Draw
|