|
|
|
@@ -39,6 +39,13 @@
|
|
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
|
|
const int firstChar = int(' ') + 1;
|
|
|
|
|
const int lastChar = 127;
|
|
|
|
|
const int numChars = lastChar-firstChar;
|
|
|
|
|
const float charPixelHeight = 20.0f;
|
|
|
|
|
|
|
|
|
|
stbtt_bakedchar chardata[numChars];
|
|
|
|
|
|
|
|
|
|
const char *GLChunkNames[] =
|
|
|
|
|
{
|
|
|
|
|
"WrappedOpenGL::Initialisation",
|
|
|
|
@@ -915,6 +922,11 @@ WrappedOpenGL::ContextData &WrappedOpenGL::GetCtxData()
|
|
|
|
|
// defined in gl_<platform>_hooks.cpp
|
|
|
|
|
void MakeContextCurrent(GLWindowingData data);
|
|
|
|
|
|
|
|
|
|
// for 'backwards compatible' overlay rendering
|
|
|
|
|
bool immediateBegin(GLenum mode, float width, float height);
|
|
|
|
|
void immediateVert(float x, float y, float u, float v);
|
|
|
|
|
void immediateEnd();
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
// Windowing/setup/etc
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
@@ -940,13 +952,14 @@ void WrappedOpenGL::DeleteContext(void *contextHandle)
|
|
|
|
|
m_ContextData.erase(contextHandle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WrappedOpenGL::CreateContext(GLWindowingData winData, void *shareContext, GLInitParams initParams, bool core)
|
|
|
|
|
void WrappedOpenGL::CreateContext(GLWindowingData winData, void *shareContext, GLInitParams initParams, bool core, bool attribsCreate)
|
|
|
|
|
{
|
|
|
|
|
// TODO: support multiple GL contexts more explicitly
|
|
|
|
|
m_InitParams = initParams;
|
|
|
|
|
|
|
|
|
|
ContextData &ctxdata = m_ContextData[winData.ctx];
|
|
|
|
|
ctxdata.isCore = core;
|
|
|
|
|
ctxdata.attribsCreate = attribsCreate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WrappedOpenGL::ActivateContext(GLWindowingData winData)
|
|
|
|
@@ -1051,6 +1064,8 @@ void WrappedOpenGL::ActivateContext(GLWindowingData winData)
|
|
|
|
|
|
|
|
|
|
int ver = mj*10 + mn;
|
|
|
|
|
|
|
|
|
|
ctxdata.version = ver;
|
|
|
|
|
|
|
|
|
|
if(ver > GLCoreVersion || (!GLIsCore && ctxdata.isCore))
|
|
|
|
|
{
|
|
|
|
|
GLCoreVersion = ver;
|
|
|
|
@@ -1059,37 +1074,20 @@ void WrappedOpenGL::ActivateContext(GLWindowingData winData)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(gl.glGenTextures && gl.glTextureStorage2DEXT && gl.glTextureSubImage2DEXT &&
|
|
|
|
|
gl.glGenVertexArrays && gl.glBindVertexArray &&
|
|
|
|
|
gl.glGenBuffers && gl.glNamedBufferStorageEXT &&
|
|
|
|
|
gl.glCreateShader && gl.glShaderSource && gl.glCompileShader && gl.glGetShaderiv && gl.glGetShaderInfoLog && gl.glDeleteShader &&
|
|
|
|
|
gl.glCreateProgram && gl.glAttachShader && gl.glLinkProgram && gl.glGetProgramiv && gl.glGetProgramInfoLog)
|
|
|
|
|
// to let us display the overlay on old GL contexts, use as simple a subset of functionality as possible
|
|
|
|
|
// to upload the texture. VAO and shaders are used optionally on modern contexts, otherwise we fall back
|
|
|
|
|
// to immediate mode rendering by hand
|
|
|
|
|
if(gl.glGetIntegerv && gl.glGenTextures && gl.glBindTexture && gl.glTexImage2D && gl.glTexParameteri)
|
|
|
|
|
{
|
|
|
|
|
gl.glGenTextures(1, &ctxdata.GlyphTexture);
|
|
|
|
|
gl.glTextureStorage2DEXT(ctxdata.GlyphTexture, eGL_TEXTURE_2D, 1, eGL_R8, FONT_TEX_WIDTH, FONT_TEX_HEIGHT);
|
|
|
|
|
|
|
|
|
|
GLuint curvao = 0;
|
|
|
|
|
gl.glGetIntegerv(eGL_VERTEX_ARRAY_BINDING, (GLint *)&curvao);
|
|
|
|
|
|
|
|
|
|
gl.glGenVertexArrays(1, &ctxdata.DummyVAO);
|
|
|
|
|
gl.glBindVertexArray(ctxdata.DummyVAO);
|
|
|
|
|
|
|
|
|
|
string ttfstring = GetEmbeddedResource(sourcecodepro_ttf);
|
|
|
|
|
byte *ttfdata = (byte *)ttfstring.c_str();
|
|
|
|
|
|
|
|
|
|
const int firstChar = int(' ') + 1;
|
|
|
|
|
const int lastChar = 127;
|
|
|
|
|
const int numChars = lastChar-firstChar;
|
|
|
|
|
|
|
|
|
|
byte *buf = new byte[FONT_TEX_WIDTH * FONT_TEX_HEIGHT];
|
|
|
|
|
|
|
|
|
|
const float pixelHeight = 20.0f;
|
|
|
|
|
stbtt_BakeFontBitmap(ttfdata, 0, charPixelHeight, buf, FONT_TEX_WIDTH, FONT_TEX_HEIGHT, firstChar, numChars, chardata);
|
|
|
|
|
|
|
|
|
|
stbtt_bakedchar chardata[numChars];
|
|
|
|
|
int ret = stbtt_BakeFontBitmap(ttfdata, 0, pixelHeight, buf, FONT_TEX_WIDTH, FONT_TEX_HEIGHT, firstChar, numChars, chardata);
|
|
|
|
|
|
|
|
|
|
ctxdata.CharSize = pixelHeight;
|
|
|
|
|
ctxdata.CharAspect = chardata->xadvance / pixelHeight;
|
|
|
|
|
ctxdata.CharSize = charPixelHeight;
|
|
|
|
|
ctxdata.CharAspect = chardata->xadvance / charPixelHeight;
|
|
|
|
|
|
|
|
|
|
stbtt_fontinfo f = {0};
|
|
|
|
|
stbtt_InitFont(&f, ttfdata, 0);
|
|
|
|
@@ -1097,10 +1095,24 @@ void WrappedOpenGL::ActivateContext(GLWindowingData winData)
|
|
|
|
|
int ascent = 0;
|
|
|
|
|
stbtt_GetFontVMetrics(&f, &ascent, NULL, NULL);
|
|
|
|
|
|
|
|
|
|
float maxheight = float(ascent)*stbtt_ScaleForPixelHeight(&f, pixelHeight);
|
|
|
|
|
float maxheight = float(ascent)*stbtt_ScaleForPixelHeight(&f, charPixelHeight);
|
|
|
|
|
|
|
|
|
|
gl.glTextureSubImage2DEXT(ctxdata.GlyphTexture, eGL_TEXTURE_2D, 0, 0, 0, FONT_TEX_WIDTH, FONT_TEX_HEIGHT,
|
|
|
|
|
eGL_RED, eGL_UNSIGNED_BYTE, (void *)buf);
|
|
|
|
|
{
|
|
|
|
|
GLuint curtex = 0;
|
|
|
|
|
gl.glGetIntegerv(eGL_TEXTURE_BINDING_2D, (GLint *)&curtex);
|
|
|
|
|
|
|
|
|
|
GLenum texFmt = eGL_R8;
|
|
|
|
|
if(ctxdata.Legacy())
|
|
|
|
|
texFmt = eGL_LUMINANCE;
|
|
|
|
|
|
|
|
|
|
gl.glGenTextures(1, &ctxdata.GlyphTexture);
|
|
|
|
|
gl.glBindTexture(eGL_TEXTURE_2D, ctxdata.GlyphTexture);
|
|
|
|
|
gl.glTexImage2D(eGL_TEXTURE_2D, 0, texFmt, FONT_TEX_WIDTH, FONT_TEX_HEIGHT, 0, eGL_RED, eGL_UNSIGNED_BYTE, buf);
|
|
|
|
|
gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MAG_FILTER, eGL_LINEAR);
|
|
|
|
|
gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MIN_FILTER, eGL_LINEAR);
|
|
|
|
|
|
|
|
|
|
gl.glBindTexture(eGL_TEXTURE_2D, curtex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete[] buf;
|
|
|
|
|
|
|
|
|
@@ -1113,72 +1125,97 @@ void WrappedOpenGL::ActivateContext(GLWindowingData winData)
|
|
|
|
|
float x = b->xoff;
|
|
|
|
|
float y = b->yoff + maxheight;
|
|
|
|
|
|
|
|
|
|
glyphData[(i+1)*2 + 0] = Vec4f(x/b->xadvance, y/pixelHeight, b->xadvance/float(b->x1 - b->x0), pixelHeight/float(b->y1 - b->y0));
|
|
|
|
|
glyphData[(i+1)*2 + 0] = Vec4f(x/b->xadvance, y/charPixelHeight, b->xadvance/float(b->x1 - b->x0), charPixelHeight/float(b->y1 - b->y0));
|
|
|
|
|
glyphData[(i+1)*2 + 1] = Vec4f(b->x0, b->y0, b->x1, b->y1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gl.glGenBuffers(1, &ctxdata.GlyphUBO);
|
|
|
|
|
gl.glNamedBufferStorageEXT(ctxdata.GlyphUBO, sizeof(glyphData), glyphData, 0);
|
|
|
|
|
|
|
|
|
|
gl.glGenBuffers(1, &ctxdata.GeneralUBO);
|
|
|
|
|
gl.glNamedBufferStorageEXT(ctxdata.GeneralUBO, sizeof(FontUniforms), NULL, GL_MAP_WRITE_BIT);
|
|
|
|
|
|
|
|
|
|
gl.glGenBuffers(1, &ctxdata.StringUBO);
|
|
|
|
|
gl.glNamedBufferStorageEXT(ctxdata.StringUBO, sizeof(uint32_t)*4*FONT_MAX_CHARS, NULL, GL_MAP_WRITE_BIT);
|
|
|
|
|
|
|
|
|
|
string textvs = "#version 420 core\n\n";
|
|
|
|
|
textvs += GetEmbeddedResource(debuguniforms_h);
|
|
|
|
|
textvs += GetEmbeddedResource(text_vert);
|
|
|
|
|
string textfs = GetEmbeddedResource(text_frag);
|
|
|
|
|
|
|
|
|
|
GLuint vs = gl.glCreateShader(eGL_VERTEX_SHADER);
|
|
|
|
|
GLuint fs = gl.glCreateShader(eGL_FRAGMENT_SHADER);
|
|
|
|
|
|
|
|
|
|
const char *src = textvs.c_str();
|
|
|
|
|
gl.glShaderSource(vs, 1, &src, NULL);
|
|
|
|
|
src = textfs.c_str();
|
|
|
|
|
gl.glShaderSource(fs, 1, &src, NULL);
|
|
|
|
|
|
|
|
|
|
gl.glCompileShader(vs);
|
|
|
|
|
gl.glCompileShader(fs);
|
|
|
|
|
|
|
|
|
|
char buffer[1024] = {0};
|
|
|
|
|
GLint status = 0;
|
|
|
|
|
|
|
|
|
|
gl.glGetShaderiv(vs, eGL_COMPILE_STATUS, &status);
|
|
|
|
|
if(status == 0)
|
|
|
|
|
if(ctxdata.Modern() && gl.glGenVertexArrays && gl.glBindVertexArray)
|
|
|
|
|
{
|
|
|
|
|
gl.glGetShaderInfoLog(vs, 1024, NULL, buffer);
|
|
|
|
|
RDCERR("Shader error: %s", buffer);
|
|
|
|
|
GLuint curvao = 0;
|
|
|
|
|
gl.glGetIntegerv(eGL_VERTEX_ARRAY_BINDING, (GLint *)&curvao);
|
|
|
|
|
|
|
|
|
|
gl.glGenVertexArrays(1, &ctxdata.DummyVAO);
|
|
|
|
|
gl.glBindVertexArray(ctxdata.DummyVAO);
|
|
|
|
|
|
|
|
|
|
gl.glBindVertexArray(curvao);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gl.glGetShaderiv(fs, eGL_COMPILE_STATUS, &status);
|
|
|
|
|
if(status == 0)
|
|
|
|
|
if(ctxdata.Modern() && gl.glGenBuffers && gl.glBufferData && gl.glBindBuffer)
|
|
|
|
|
{
|
|
|
|
|
gl.glGetShaderInfoLog(fs, 1024, NULL, buffer);
|
|
|
|
|
RDCERR("Shader error: %s", buffer);
|
|
|
|
|
GLuint curubo = 0;
|
|
|
|
|
gl.glGetIntegerv(eGL_UNIFORM_BUFFER_BINDING, (GLint *)&curubo);
|
|
|
|
|
|
|
|
|
|
gl.glGenBuffers(1, &ctxdata.GlyphUBO);
|
|
|
|
|
gl.glBindBuffer(eGL_UNIFORM_BUFFER, ctxdata.GlyphUBO);
|
|
|
|
|
gl.glBufferData(eGL_UNIFORM_BUFFER, sizeof(glyphData), glyphData, eGL_STATIC_DRAW);
|
|
|
|
|
|
|
|
|
|
gl.glGenBuffers(1, &ctxdata.GeneralUBO);
|
|
|
|
|
gl.glBindBuffer(eGL_UNIFORM_BUFFER, ctxdata.GeneralUBO);
|
|
|
|
|
gl.glBufferData(eGL_UNIFORM_BUFFER, sizeof(FontUniforms), NULL, eGL_DYNAMIC_DRAW);
|
|
|
|
|
|
|
|
|
|
gl.glGenBuffers(1, &ctxdata.StringUBO);
|
|
|
|
|
gl.glBindBuffer(eGL_UNIFORM_BUFFER, ctxdata.StringUBO);
|
|
|
|
|
gl.glBufferData(eGL_UNIFORM_BUFFER, sizeof(uint32_t)*4*FONT_MAX_CHARS, NULL, eGL_DYNAMIC_DRAW);
|
|
|
|
|
|
|
|
|
|
gl.glBindBuffer(eGL_UNIFORM_BUFFER, curubo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctxdata.Program = gl.glCreateProgram();
|
|
|
|
|
|
|
|
|
|
gl.glAttachShader(ctxdata.Program, vs);
|
|
|
|
|
gl.glAttachShader(ctxdata.Program, fs);
|
|
|
|
|
|
|
|
|
|
gl.glLinkProgram(ctxdata.Program);
|
|
|
|
|
|
|
|
|
|
gl.glGetProgramiv(ctxdata.Program, eGL_LINK_STATUS, &status);
|
|
|
|
|
if(status == 0)
|
|
|
|
|
if(ctxdata.Modern() &&
|
|
|
|
|
gl.glCreateShader && gl.glShaderSource && gl.glCompileShader && gl.glGetShaderiv && gl.glGetShaderInfoLog && gl.glDeleteShader &&
|
|
|
|
|
gl.glCreateProgram && gl.glAttachShader && gl.glLinkProgram && gl.glGetProgramiv && gl.glGetProgramInfoLog)
|
|
|
|
|
{
|
|
|
|
|
gl.glGetProgramInfoLog(ctxdata.Program, 1024, NULL, buffer);
|
|
|
|
|
RDCERR("Link error: %s", buffer);
|
|
|
|
|
}
|
|
|
|
|
string textvs = "#version 420 core\n\n";
|
|
|
|
|
textvs += GetEmbeddedResource(debuguniforms_h);
|
|
|
|
|
textvs += GetEmbeddedResource(text_vert);
|
|
|
|
|
string textfs = GetEmbeddedResource(text_frag);
|
|
|
|
|
|
|
|
|
|
gl.glDeleteShader(vs);
|
|
|
|
|
gl.glDeleteShader(fs);
|
|
|
|
|
GLuint vs = gl.glCreateShader(eGL_VERTEX_SHADER);
|
|
|
|
|
GLuint fs = gl.glCreateShader(eGL_FRAGMENT_SHADER);
|
|
|
|
|
|
|
|
|
|
const char *src = textvs.c_str();
|
|
|
|
|
gl.glShaderSource(vs, 1, &src, NULL);
|
|
|
|
|
src = textfs.c_str();
|
|
|
|
|
gl.glShaderSource(fs, 1, &src, NULL);
|
|
|
|
|
|
|
|
|
|
gl.glCompileShader(vs);
|
|
|
|
|
gl.glCompileShader(fs);
|
|
|
|
|
|
|
|
|
|
char buffer[1024] = {0};
|
|
|
|
|
GLint status = 0;
|
|
|
|
|
|
|
|
|
|
gl.glGetShaderiv(vs, eGL_COMPILE_STATUS, &status);
|
|
|
|
|
if(status == 0)
|
|
|
|
|
{
|
|
|
|
|
gl.glGetShaderInfoLog(vs, 1024, NULL, buffer);
|
|
|
|
|
RDCERR("Shader error: %s", buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gl.glGetShaderiv(fs, eGL_COMPILE_STATUS, &status);
|
|
|
|
|
if(status == 0)
|
|
|
|
|
{
|
|
|
|
|
gl.glGetShaderInfoLog(fs, 1024, NULL, buffer);
|
|
|
|
|
RDCERR("Shader error: %s", buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctxdata.Program = gl.glCreateProgram();
|
|
|
|
|
|
|
|
|
|
gl.glAttachShader(ctxdata.Program, vs);
|
|
|
|
|
gl.glAttachShader(ctxdata.Program, fs);
|
|
|
|
|
|
|
|
|
|
gl.glLinkProgram(ctxdata.Program);
|
|
|
|
|
|
|
|
|
|
gl.glGetProgramiv(ctxdata.Program, eGL_LINK_STATUS, &status);
|
|
|
|
|
if(status == 0)
|
|
|
|
|
{
|
|
|
|
|
gl.glGetProgramInfoLog(ctxdata.Program, 1024, NULL, buffer);
|
|
|
|
|
RDCERR("Link error: %s", buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gl.glDeleteShader(vs);
|
|
|
|
|
gl.glDeleteShader(fs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctxdata.ready = true;
|
|
|
|
|
|
|
|
|
|
gl.glBindVertexArray(curvao);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -1202,23 +1239,46 @@ struct RenderTextState
|
|
|
|
|
GLenum SourceRGB, SourceAlpha;
|
|
|
|
|
GLenum DestinationRGB, DestinationAlpha;
|
|
|
|
|
GLenum PolygonMode;
|
|
|
|
|
GLfloat Viewport[4];
|
|
|
|
|
GLfloat Viewportf[4];
|
|
|
|
|
GLint Viewport[4];
|
|
|
|
|
GLenum ActiveTexture;
|
|
|
|
|
GLuint tex0;
|
|
|
|
|
GLuint ubo[3];
|
|
|
|
|
GLuint prog;
|
|
|
|
|
GLuint pipe;
|
|
|
|
|
GLuint VAO;
|
|
|
|
|
|
|
|
|
|
void Push(const GLHookSet &gl)
|
|
|
|
|
// if this context wasn't created with CreateContextAttribs we
|
|
|
|
|
// do an immediate mode render, so fewer states are pushed/popped.
|
|
|
|
|
// note we don't assume a 1.0 context since that would be painful to
|
|
|
|
|
// handle. Instead we just skip bits of state we're not going to mess
|
|
|
|
|
// with. In some cases this might cause problems e.g. we don't use
|
|
|
|
|
// indexed enable states for blend and scissor test because we're
|
|
|
|
|
// assuming there's no separate blending.
|
|
|
|
|
//
|
|
|
|
|
// In the end, this is just a best-effort to keep going without
|
|
|
|
|
// crashing. Old GL versions aren't supported.
|
|
|
|
|
void Push(const GLHookSet &gl, bool modern)
|
|
|
|
|
{
|
|
|
|
|
enableBits[0] = gl.glIsEnabledi(eGL_BLEND, 0) != 0;
|
|
|
|
|
enableBits[1] = gl.glIsEnabled(eGL_DEPTH_TEST) != 0;
|
|
|
|
|
enableBits[2] = gl.glIsEnabled(eGL_DEPTH_CLAMP) != 0;
|
|
|
|
|
enableBits[3] = gl.glIsEnabled(eGL_STENCIL_TEST) != 0;
|
|
|
|
|
enableBits[4] = gl.glIsEnabled(eGL_CULL_FACE) != 0;
|
|
|
|
|
enableBits[5] = gl.glIsEnabledi(eGL_SCISSOR_TEST, 0) != 0;
|
|
|
|
|
|
|
|
|
|
if(GLCoreVersion >= 45 || ExtensionSupported[ExtensionSupported_ARB_clip_control])
|
|
|
|
|
enableBits[0] = gl.glIsEnabled(eGL_DEPTH_TEST) != 0;
|
|
|
|
|
enableBits[1] = gl.glIsEnabled(eGL_STENCIL_TEST) != 0;
|
|
|
|
|
enableBits[2] = gl.glIsEnabled(eGL_CULL_FACE) != 0;
|
|
|
|
|
if(modern)
|
|
|
|
|
{
|
|
|
|
|
enableBits[3] = gl.glIsEnabled(eGL_DEPTH_CLAMP) != 0;
|
|
|
|
|
enableBits[4] = gl.glIsEnabledi(eGL_BLEND, 0) != 0;
|
|
|
|
|
enableBits[5] = gl.glIsEnabledi(eGL_SCISSOR_TEST, 0) != 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
enableBits[3] = gl.glIsEnabled(eGL_BLEND) != 0;
|
|
|
|
|
enableBits[4] = gl.glIsEnabled(eGL_SCISSOR_TEST) != 0;
|
|
|
|
|
enableBits[5] = gl.glIsEnabled(eGL_TEXTURE_2D) != 0;
|
|
|
|
|
enableBits[6] = gl.glIsEnabled(eGL_LIGHTING) != 0;
|
|
|
|
|
enableBits[7] = gl.glIsEnabled(eGL_ALPHA_TEST) != 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(modern && (GLCoreVersion >= 45 || ExtensionSupported[ExtensionSupported_ARB_clip_control]))
|
|
|
|
|
{
|
|
|
|
|
gl.glGetIntegerv(eGL_CLIP_ORIGIN, (GLint *)&ClipOrigin);
|
|
|
|
|
gl.glGetIntegerv(eGL_CLIP_DEPTH_MODE, (GLint *)&ClipDepth);
|
|
|
|
@@ -1229,15 +1289,29 @@ struct RenderTextState
|
|
|
|
|
ClipDepth = eGL_NEGATIVE_ONE_TO_ONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gl.glGetIntegeri_v(eGL_BLEND_EQUATION_RGB, 0, (GLint*)&EquationRGB);
|
|
|
|
|
gl.glGetIntegeri_v(eGL_BLEND_EQUATION_ALPHA, 0, (GLint*)&EquationAlpha);
|
|
|
|
|
if(modern)
|
|
|
|
|
{
|
|
|
|
|
gl.glGetIntegeri_v(eGL_BLEND_EQUATION_RGB, 0, (GLint*)&EquationRGB);
|
|
|
|
|
gl.glGetIntegeri_v(eGL_BLEND_EQUATION_ALPHA, 0, (GLint*)&EquationAlpha);
|
|
|
|
|
|
|
|
|
|
gl.glGetIntegeri_v(eGL_BLEND_SRC_RGB, 0, (GLint*)&SourceRGB);
|
|
|
|
|
gl.glGetIntegeri_v(eGL_BLEND_SRC_ALPHA, 0, (GLint*)&SourceAlpha);
|
|
|
|
|
gl.glGetIntegeri_v(eGL_BLEND_SRC_RGB, 0, (GLint*)&SourceRGB);
|
|
|
|
|
gl.glGetIntegeri_v(eGL_BLEND_SRC_ALPHA, 0, (GLint*)&SourceAlpha);
|
|
|
|
|
|
|
|
|
|
gl.glGetIntegeri_v(eGL_BLEND_DST_RGB, 0, (GLint*)&DestinationRGB);
|
|
|
|
|
gl.glGetIntegeri_v(eGL_BLEND_DST_ALPHA, 0, (GLint*)&DestinationAlpha);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
gl.glGetIntegerv(eGL_BLEND_EQUATION_RGB, (GLint*)&EquationRGB);
|
|
|
|
|
gl.glGetIntegerv(eGL_BLEND_EQUATION_ALPHA, (GLint*)&EquationAlpha);
|
|
|
|
|
|
|
|
|
|
gl.glGetIntegerv(eGL_BLEND_SRC_RGB, (GLint*)&SourceRGB);
|
|
|
|
|
gl.glGetIntegerv(eGL_BLEND_SRC_ALPHA, (GLint*)&SourceAlpha);
|
|
|
|
|
|
|
|
|
|
gl.glGetIntegerv(eGL_BLEND_DST_RGB, (GLint*)&DestinationRGB);
|
|
|
|
|
gl.glGetIntegerv(eGL_BLEND_DST_ALPHA, (GLint*)&DestinationAlpha);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gl.glGetIntegeri_v(eGL_BLEND_DST_RGB, 0, (GLint*)&DestinationRGB);
|
|
|
|
|
gl.glGetIntegeri_v(eGL_BLEND_DST_ALPHA, 0, (GLint*)&DestinationAlpha);
|
|
|
|
|
|
|
|
|
|
if(!VendorCheck[VendorCheck_AMD_polygon_mode_query])
|
|
|
|
|
{
|
|
|
|
|
GLenum dummy[2] = { eGL_FILL, eGL_FILL };
|
|
|
|
@@ -1250,52 +1324,99 @@ struct RenderTextState
|
|
|
|
|
{
|
|
|
|
|
PolygonMode = eGL_FILL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gl.glGetFloati_v(eGL_VIEWPORT, 0, &Viewport[0]);
|
|
|
|
|
|
|
|
|
|
if(modern)
|
|
|
|
|
gl.glGetFloati_v(eGL_VIEWPORT, 0, &Viewportf[0]);
|
|
|
|
|
else
|
|
|
|
|
gl.glGetIntegerv(eGL_VIEWPORT, &Viewport[0]);
|
|
|
|
|
|
|
|
|
|
gl.glGetIntegerv(eGL_ACTIVE_TEXTURE, (GLint *)&ActiveTexture);
|
|
|
|
|
gl.glActiveTexture(eGL_TEXTURE0);
|
|
|
|
|
gl.glGetIntegerv(eGL_TEXTURE_BINDING_2D, (GLint*)&tex0);
|
|
|
|
|
|
|
|
|
|
gl.glGetIntegeri_v(eGL_UNIFORM_BUFFER_BINDING, 0, (GLint*)&ubo[0]);
|
|
|
|
|
gl.glGetIntegeri_v(eGL_UNIFORM_BUFFER_BINDING, 1, (GLint*)&ubo[1]);
|
|
|
|
|
gl.glGetIntegeri_v(eGL_UNIFORM_BUFFER_BINDING, 2, (GLint*)&ubo[2]);
|
|
|
|
|
|
|
|
|
|
// we get the current program but only try to restore it if it's non-0
|
|
|
|
|
prog = 0;
|
|
|
|
|
gl.glGetIntegerv(eGL_CURRENT_PROGRAM, (GLint *)&prog);
|
|
|
|
|
|
|
|
|
|
gl.glGetIntegerv(eGL_VERTEX_ARRAY_BINDING, (GLint *)&VAO);
|
|
|
|
|
|
|
|
|
|
// since we will use the fixed function pipeline, also need to check for
|
|
|
|
|
// program pipeline bindings (if we weren't, our program would override)
|
|
|
|
|
pipe = 0;
|
|
|
|
|
gl.glGetIntegerv(eGL_PROGRAM_PIPELINE_BINDING, (GLint *)&pipe);
|
|
|
|
|
|
|
|
|
|
if(modern)
|
|
|
|
|
{
|
|
|
|
|
gl.glGetIntegeri_v(eGL_UNIFORM_BUFFER_BINDING, 0, (GLint*)&ubo[0]);
|
|
|
|
|
gl.glGetIntegeri_v(eGL_UNIFORM_BUFFER_BINDING, 1, (GLint*)&ubo[1]);
|
|
|
|
|
gl.glGetIntegeri_v(eGL_UNIFORM_BUFFER_BINDING, 2, (GLint*)&ubo[2]);
|
|
|
|
|
|
|
|
|
|
gl.glGetIntegerv(eGL_VERTEX_ARRAY_BINDING, (GLint *)&VAO);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Pop(const GLHookSet &gl)
|
|
|
|
|
void Pop(const GLHookSet &gl, bool modern)
|
|
|
|
|
{
|
|
|
|
|
if(enableBits[0]) gl.glEnablei(eGL_BLEND, 0); else gl.glDisablei(eGL_BLEND, 0);
|
|
|
|
|
if(enableBits[1]) gl.glEnable(eGL_DEPTH_TEST); else gl.glDisable(eGL_DEPTH_TEST);
|
|
|
|
|
if(enableBits[2]) gl.glEnable(eGL_DEPTH_CLAMP); else gl.glDisable(eGL_DEPTH_CLAMP);
|
|
|
|
|
if(enableBits[3]) gl.glEnable(eGL_STENCIL_TEST); else gl.glDisable(eGL_STENCIL_TEST);
|
|
|
|
|
if(enableBits[4]) gl.glEnable(eGL_CULL_FACE); else gl.glDisable(eGL_CULL_FACE);
|
|
|
|
|
if(enableBits[5]) gl.glEnablei(eGL_SCISSOR_TEST, 0); else gl.glDisablei(eGL_SCISSOR_TEST, 0);
|
|
|
|
|
if(enableBits[0]) gl.glEnable(eGL_DEPTH_TEST); else gl.glDisable(eGL_DEPTH_TEST);
|
|
|
|
|
if(enableBits[1]) gl.glEnable(eGL_STENCIL_TEST); else gl.glDisable(eGL_STENCIL_TEST);
|
|
|
|
|
if(enableBits[2]) gl.glEnable(eGL_CULL_FACE); else gl.glDisable(eGL_CULL_FACE);
|
|
|
|
|
|
|
|
|
|
if(gl.glClipControl && (GLCoreVersion >= 45 || ExtensionSupported[ExtensionSupported_ARB_clip_control])) // only available in 4.5+
|
|
|
|
|
if(modern)
|
|
|
|
|
{
|
|
|
|
|
if(enableBits[3]) gl.glEnable(eGL_DEPTH_CLAMP); else gl.glDisable(eGL_DEPTH_CLAMP);
|
|
|
|
|
if(enableBits[4]) gl.glEnablei(eGL_BLEND, 0); else gl.glDisablei(eGL_BLEND, 0);
|
|
|
|
|
if(enableBits[5]) gl.glEnablei(eGL_SCISSOR_TEST, 0); else gl.glDisablei(eGL_SCISSOR_TEST, 0);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(enableBits[3]) gl.glEnable(eGL_BLEND); else gl.glDisable(eGL_BLEND);
|
|
|
|
|
if(enableBits[4]) gl.glEnable(eGL_SCISSOR_TEST); else gl.glDisable(eGL_SCISSOR_TEST);
|
|
|
|
|
if(enableBits[5]) gl.glEnable(eGL_TEXTURE_2D); else gl.glDisable(eGL_TEXTURE_2D);
|
|
|
|
|
if(enableBits[6]) gl.glEnable(eGL_LIGHTING); else gl.glDisable(eGL_LIGHTING);
|
|
|
|
|
if(enableBits[7]) gl.glEnable(eGL_ALPHA_TEST); else gl.glDisable(eGL_ALPHA_TEST);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(modern && gl.glClipControl && (GLCoreVersion >= 45 || ExtensionSupported[ExtensionSupported_ARB_clip_control])) // only available in 4.5+
|
|
|
|
|
gl.glClipControl(ClipOrigin, ClipDepth);
|
|
|
|
|
|
|
|
|
|
gl.glBlendFuncSeparatei(0, SourceRGB, DestinationRGB, SourceAlpha, DestinationAlpha);
|
|
|
|
|
gl.glBlendEquationSeparatei(0, EquationRGB, EquationAlpha);
|
|
|
|
|
if(modern)
|
|
|
|
|
{
|
|
|
|
|
gl.glBlendFuncSeparatei(0, SourceRGB, DestinationRGB, SourceAlpha, DestinationAlpha);
|
|
|
|
|
gl.glBlendEquationSeparatei(0, EquationRGB, EquationAlpha);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
gl.glBlendFuncSeparate(SourceRGB, DestinationRGB, SourceAlpha, DestinationAlpha);
|
|
|
|
|
gl.glBlendEquationSeparate(EquationRGB, EquationAlpha);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gl.glPolygonMode(eGL_FRONT_AND_BACK, PolygonMode);
|
|
|
|
|
|
|
|
|
|
gl.glViewportIndexedf(0, Viewport[0], Viewport[1], Viewport[2], Viewport[3]);
|
|
|
|
|
if(modern)
|
|
|
|
|
gl.glViewportIndexedf(0, Viewportf[0], Viewportf[1], Viewportf[2], Viewportf[3]);
|
|
|
|
|
else
|
|
|
|
|
gl.glViewport(Viewport[0], Viewport[1], (GLsizei)Viewport[2], (GLsizei)Viewport[3]);
|
|
|
|
|
|
|
|
|
|
gl.glActiveTexture(eGL_TEXTURE0);
|
|
|
|
|
gl.glBindTexture(eGL_TEXTURE_2D, tex0);
|
|
|
|
|
gl.glActiveTexture(ActiveTexture);
|
|
|
|
|
|
|
|
|
|
gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 0, ubo[0]);
|
|
|
|
|
gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 1, ubo[1]);
|
|
|
|
|
gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 2, ubo[2]);
|
|
|
|
|
|
|
|
|
|
gl.glUseProgram(prog);
|
|
|
|
|
|
|
|
|
|
gl.glBindVertexArray(VAO);
|
|
|
|
|
if(modern)
|
|
|
|
|
{
|
|
|
|
|
gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 0, ubo[0]);
|
|
|
|
|
gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 1, ubo[1]);
|
|
|
|
|
gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 2, ubo[2]);
|
|
|
|
|
|
|
|
|
|
gl.glUseProgram(prog);
|
|
|
|
|
|
|
|
|
|
gl.glBindVertexArray(VAO);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// only restore these if there was a setting and the function pointer exists
|
|
|
|
|
if(gl.glUseProgram && prog != 0)
|
|
|
|
|
gl.glUseProgram(prog);
|
|
|
|
|
if(gl.glBindProgramPipeline && pipe != 0)
|
|
|
|
|
gl.glBindProgramPipeline(pipe);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@@ -1334,106 +1455,220 @@ void WrappedOpenGL::RenderOverlayStr(float x, float y, const char *text)
|
|
|
|
|
|
|
|
|
|
if(!ctxdata.built || !ctxdata.ready) return;
|
|
|
|
|
|
|
|
|
|
gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 0, ctxdata.GeneralUBO);
|
|
|
|
|
|
|
|
|
|
FontUniforms *ubo = (FontUniforms *)gl.glMapBufferRange(eGL_UNIFORM_BUFFER, 0, sizeof(FontUniforms), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
|
|
|
|
|
ubo->TextPosition.x = x;
|
|
|
|
|
ubo->TextPosition.y = y;
|
|
|
|
|
|
|
|
|
|
ubo->FontScreenAspect.x = 1.0f/float(m_InitParams.width);
|
|
|
|
|
ubo->FontScreenAspect.y = 1.0f/float(m_InitParams.height);
|
|
|
|
|
|
|
|
|
|
ubo->TextSize = ctxdata.CharSize;
|
|
|
|
|
ubo->FontScreenAspect.x *= ctxdata.CharAspect;
|
|
|
|
|
|
|
|
|
|
ubo->CharacterSize.x = 1.0f/float(FONT_TEX_WIDTH);
|
|
|
|
|
ubo->CharacterSize.y = 1.0f/float(FONT_TEX_HEIGHT);
|
|
|
|
|
|
|
|
|
|
gl.glUnmapBuffer(eGL_UNIFORM_BUFFER);
|
|
|
|
|
|
|
|
|
|
size_t len = strlen(text);
|
|
|
|
|
|
|
|
|
|
if((int)len > FONT_MAX_CHARS)
|
|
|
|
|
// if it's reasonably modern context, assume we can use buffers and UBOs
|
|
|
|
|
if(ctxdata.Modern())
|
|
|
|
|
{
|
|
|
|
|
static bool printedWarning = false;
|
|
|
|
|
gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 0, ctxdata.GeneralUBO);
|
|
|
|
|
|
|
|
|
|
// this could be called once a frame, don't want to spam the log
|
|
|
|
|
if(!printedWarning)
|
|
|
|
|
FontUniforms *ubo = (FontUniforms *)gl.glMapBufferRange(eGL_UNIFORM_BUFFER, 0, sizeof(FontUniforms), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
|
|
|
|
|
ubo->TextPosition.x = x;
|
|
|
|
|
ubo->TextPosition.y = y;
|
|
|
|
|
|
|
|
|
|
ubo->FontScreenAspect.x = 1.0f/float(m_InitParams.width);
|
|
|
|
|
ubo->FontScreenAspect.y = 1.0f/float(m_InitParams.height);
|
|
|
|
|
|
|
|
|
|
ubo->TextSize = ctxdata.CharSize;
|
|
|
|
|
ubo->FontScreenAspect.x *= ctxdata.CharAspect;
|
|
|
|
|
|
|
|
|
|
ubo->CharacterSize.x = 1.0f/float(FONT_TEX_WIDTH);
|
|
|
|
|
ubo->CharacterSize.y = 1.0f/float(FONT_TEX_HEIGHT);
|
|
|
|
|
|
|
|
|
|
gl.glUnmapBuffer(eGL_UNIFORM_BUFFER);
|
|
|
|
|
|
|
|
|
|
size_t len = strlen(text);
|
|
|
|
|
|
|
|
|
|
if((int)len > FONT_MAX_CHARS)
|
|
|
|
|
{
|
|
|
|
|
printedWarning = true;
|
|
|
|
|
RDCWARN("log string '%s' is too long", text, (int)len);
|
|
|
|
|
static bool printedWarning = false;
|
|
|
|
|
|
|
|
|
|
// this could be called once a frame, don't want to spam the log
|
|
|
|
|
if(!printedWarning)
|
|
|
|
|
{
|
|
|
|
|
printedWarning = true;
|
|
|
|
|
RDCWARN("log string '%s' is too long", text, (int)len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
len = FONT_MAX_CHARS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
len = FONT_MAX_CHARS;
|
|
|
|
|
}
|
|
|
|
|
gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 0, ctxdata.StringUBO);
|
|
|
|
|
uint32_t *texs = (uint32_t *)gl.glMapBufferRange(eGL_UNIFORM_BUFFER, 0, len*4*sizeof(uint32_t), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
|
|
|
|
|
|
|
|
|
|
gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 0, ctxdata.StringUBO);
|
|
|
|
|
uint32_t *texs = (uint32_t *)gl.glMapBufferRange(eGL_UNIFORM_BUFFER, 0, len*4*sizeof(uint32_t), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
|
|
|
|
|
|
|
|
|
|
if(texs)
|
|
|
|
|
{
|
|
|
|
|
for(size_t i=0; i < len; i++)
|
|
|
|
|
if(texs)
|
|
|
|
|
{
|
|
|
|
|
texs[i*4+0] = text[i] - ' ';
|
|
|
|
|
texs[i*4+1] = text[i] - ' ';
|
|
|
|
|
texs[i*4+2] = text[i] - ' ';
|
|
|
|
|
texs[i*4+3] = text[i] - ' ';
|
|
|
|
|
for(size_t i=0; i < len; i++)
|
|
|
|
|
{
|
|
|
|
|
texs[i*4+0] = text[i] - ' ';
|
|
|
|
|
texs[i*4+1] = text[i] - ' ';
|
|
|
|
|
texs[i*4+2] = text[i] - ' ';
|
|
|
|
|
texs[i*4+3] = text[i] - ' ';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
static bool printedWarning = false;
|
|
|
|
|
|
|
|
|
|
// this could be called once a frame, don't want to spam the log
|
|
|
|
|
if(!printedWarning)
|
|
|
|
|
{
|
|
|
|
|
printedWarning = true;
|
|
|
|
|
RDCWARN("failed to map %d characters for '%s' (%d)", (int)len, text, ctxdata.StringUBO);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gl.glUnmapBuffer(eGL_UNIFORM_BUFFER);
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Make sure if you change any other state in here, that you also update the push
|
|
|
|
|
// and pop functions above (RenderTextState)
|
|
|
|
|
|
|
|
|
|
// set blend state
|
|
|
|
|
gl.glEnablei(eGL_BLEND, 0);
|
|
|
|
|
gl.glBlendFuncSeparatei(0, eGL_SRC_ALPHA, eGL_ONE_MINUS_SRC_ALPHA, eGL_SRC_ALPHA, eGL_SRC_ALPHA);
|
|
|
|
|
gl.glBlendEquationSeparatei(0, eGL_FUNC_ADD, eGL_FUNC_ADD);
|
|
|
|
|
|
|
|
|
|
// set depth & stencil
|
|
|
|
|
gl.glDisable(eGL_DEPTH_TEST);
|
|
|
|
|
gl.glDisable(eGL_DEPTH_CLAMP);
|
|
|
|
|
gl.glDisable(eGL_STENCIL_TEST);
|
|
|
|
|
gl.glDisable(eGL_CULL_FACE);
|
|
|
|
|
|
|
|
|
|
// set viewport & scissor
|
|
|
|
|
gl.glViewportIndexedf(0, 0.0f, 0.0f, (float)m_InitParams.width, (float)m_InitParams.height);
|
|
|
|
|
gl.glDisablei(eGL_SCISSOR_TEST, 0);
|
|
|
|
|
gl.glPolygonMode(eGL_FRONT_AND_BACK, eGL_FILL);
|
|
|
|
|
|
|
|
|
|
if(gl.glClipControl && (GLCoreVersion >= 45 || ExtensionSupported[ExtensionSupported_ARB_clip_control])) // only available in 4.5+
|
|
|
|
|
gl.glClipControl(eGL_LOWER_LEFT, eGL_NEGATIVE_ONE_TO_ONE);
|
|
|
|
|
|
|
|
|
|
// bind UBOs
|
|
|
|
|
gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 0, ctxdata.GeneralUBO);
|
|
|
|
|
gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 1, ctxdata.GlyphUBO);
|
|
|
|
|
gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 2, ctxdata.StringUBO);
|
|
|
|
|
|
|
|
|
|
// bind empty VAO just for valid rendering
|
|
|
|
|
gl.glBindVertexArray(ctxdata.DummyVAO);
|
|
|
|
|
|
|
|
|
|
// bind textures
|
|
|
|
|
gl.glActiveTexture(eGL_TEXTURE0);
|
|
|
|
|
gl.glBindTexture(eGL_TEXTURE_2D, ctxdata.GlyphTexture);
|
|
|
|
|
|
|
|
|
|
// bind program
|
|
|
|
|
gl.glUseProgram(ctxdata.Program);
|
|
|
|
|
|
|
|
|
|
// draw string
|
|
|
|
|
gl.glDrawArraysInstanced(eGL_TRIANGLE_STRIP, 0, 4, (GLsizei)len);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
static bool printedWarning = false;
|
|
|
|
|
// if it wasn't created in modern fashion with createattribs, assume the worst
|
|
|
|
|
// and draw with immediate mode (since it's impossible that the context is core
|
|
|
|
|
// profile, this will always work)
|
|
|
|
|
//
|
|
|
|
|
// This isn't perfect since without a lot of fiddling we'd need to check if e.g.
|
|
|
|
|
// indexed blending should be used or not. Since we're not too worried about
|
|
|
|
|
// working in this situation, just doing something reasonable, we just assume
|
|
|
|
|
// roughly ~2.0 functionality
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Make sure if you change any other state in here, that you also update the push
|
|
|
|
|
// and pop functions above (RenderTextState)
|
|
|
|
|
|
|
|
|
|
// this could be called once a frame, don't want to spam the log
|
|
|
|
|
if(!printedWarning)
|
|
|
|
|
// disable blending and some old-style fixed function features
|
|
|
|
|
gl.glDisable(eGL_BLEND);
|
|
|
|
|
gl.glDisable(eGL_LIGHTING);
|
|
|
|
|
gl.glDisable(eGL_ALPHA_TEST);
|
|
|
|
|
|
|
|
|
|
// set depth & stencil
|
|
|
|
|
gl.glDisable(eGL_DEPTH_TEST);
|
|
|
|
|
gl.glDisable(eGL_STENCIL_TEST);
|
|
|
|
|
gl.glDisable(eGL_CULL_FACE);
|
|
|
|
|
|
|
|
|
|
// set viewport & scissor
|
|
|
|
|
gl.glViewport(0, 0, (GLsizei)m_InitParams.width, (GLsizei)m_InitParams.height);
|
|
|
|
|
gl.glDisable(eGL_SCISSOR_TEST);
|
|
|
|
|
gl.glPolygonMode(eGL_FRONT_AND_BACK, eGL_FILL);
|
|
|
|
|
|
|
|
|
|
// bind textures
|
|
|
|
|
gl.glActiveTexture(eGL_TEXTURE0);
|
|
|
|
|
gl.glBindTexture(eGL_TEXTURE_2D, ctxdata.GlyphTexture);
|
|
|
|
|
gl.glEnable(eGL_TEXTURE_2D);
|
|
|
|
|
|
|
|
|
|
// just in case, try to disable the programmable pipeline
|
|
|
|
|
if(gl.glUseProgram)
|
|
|
|
|
gl.glUseProgram(0);
|
|
|
|
|
if(gl.glBindProgramPipeline)
|
|
|
|
|
gl.glBindProgramPipeline(0);
|
|
|
|
|
|
|
|
|
|
// draw string (based on sample code from stb_truetype.h)
|
|
|
|
|
if(immediateBegin(eGL_QUADS, (float)m_InitParams.width, (float)m_InitParams.height))
|
|
|
|
|
{
|
|
|
|
|
printedWarning = true;
|
|
|
|
|
RDCWARN("failed to map %d characters for '%s' (%d)", (int)len, text, ctxdata.StringUBO);
|
|
|
|
|
y += 1.0f;
|
|
|
|
|
y *= charPixelHeight;
|
|
|
|
|
|
|
|
|
|
float startx = x;
|
|
|
|
|
float starty = y;
|
|
|
|
|
|
|
|
|
|
float maxx = x, minx = x;
|
|
|
|
|
float maxy = y, miny = y - charPixelHeight;
|
|
|
|
|
|
|
|
|
|
stbtt_aligned_quad q;
|
|
|
|
|
|
|
|
|
|
const char *prepass = text;
|
|
|
|
|
while (*prepass)
|
|
|
|
|
{
|
|
|
|
|
char c = *prepass;
|
|
|
|
|
if (c >= firstChar && c <= lastChar)
|
|
|
|
|
{
|
|
|
|
|
stbtt_GetBakedQuad(chardata, FONT_TEX_WIDTH, FONT_TEX_HEIGHT, c-firstChar, &x, &y, &q, 1);
|
|
|
|
|
|
|
|
|
|
maxx = RDCMAX(maxx, RDCMAX(q.x0, q.x1));
|
|
|
|
|
maxy = RDCMAX(maxy, RDCMAX(q.y0, q.y1));
|
|
|
|
|
|
|
|
|
|
minx = RDCMIN(minx, RDCMIN(q.x0, q.x1));
|
|
|
|
|
miny = RDCMIN(miny, RDCMIN(q.y0, q.y1));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
x += chardata[0].xadvance;
|
|
|
|
|
}
|
|
|
|
|
prepass++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
x = startx;
|
|
|
|
|
y = starty;
|
|
|
|
|
|
|
|
|
|
// draw black bar behind text
|
|
|
|
|
immediateVert(minx, maxy, 0.0f, 0.0f);
|
|
|
|
|
immediateVert(maxx, maxy, 0.0f, 0.0f);
|
|
|
|
|
immediateVert(maxx, miny, 0.0f, 0.0f);
|
|
|
|
|
immediateVert(minx, miny, 0.0f, 0.0f);
|
|
|
|
|
|
|
|
|
|
while (*text)
|
|
|
|
|
{
|
|
|
|
|
char c = *text;
|
|
|
|
|
if (c >= firstChar && c <= lastChar)
|
|
|
|
|
{
|
|
|
|
|
stbtt_GetBakedQuad(chardata, FONT_TEX_WIDTH, FONT_TEX_HEIGHT, c-firstChar, &x, &y, &q, 1);
|
|
|
|
|
|
|
|
|
|
immediateVert(q.x0, q.y0, q.s0, q.t0);
|
|
|
|
|
immediateVert(q.x1, q.y0, q.s1, q.t0);
|
|
|
|
|
immediateVert(q.x1, q.y1, q.s1, q.t1);
|
|
|
|
|
immediateVert(q.x0, q.y1, q.s0, q.t1);
|
|
|
|
|
|
|
|
|
|
maxx = RDCMAX(maxx, RDCMAX(q.x0, q.x1));
|
|
|
|
|
maxy = RDCMAX(maxy, RDCMAX(q.y0, q.y1));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
x += chardata[0].xadvance;
|
|
|
|
|
}
|
|
|
|
|
++text;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
immediateEnd();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gl.glUnmapBuffer(eGL_UNIFORM_BUFFER);
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Make sure if you change any other state in here, that you also update the push
|
|
|
|
|
// and pop functions above (RenderTextState)
|
|
|
|
|
|
|
|
|
|
// set blend state
|
|
|
|
|
gl.glEnablei(eGL_BLEND, 0);
|
|
|
|
|
gl.glBlendFuncSeparatei(0, eGL_SRC_ALPHA, eGL_ONE_MINUS_SRC_ALPHA, eGL_SRC_ALPHA, eGL_SRC_ALPHA);
|
|
|
|
|
gl.glBlendEquationSeparatei(0, eGL_FUNC_ADD, eGL_FUNC_ADD);
|
|
|
|
|
|
|
|
|
|
// set depth & stencil
|
|
|
|
|
gl.glDisable(eGL_DEPTH_TEST);
|
|
|
|
|
gl.glDisable(eGL_DEPTH_CLAMP);
|
|
|
|
|
gl.glDisable(eGL_STENCIL_TEST);
|
|
|
|
|
gl.glDisable(eGL_CULL_FACE);
|
|
|
|
|
|
|
|
|
|
// set viewport & scissor
|
|
|
|
|
gl.glViewportIndexedf(0, 0.0f, 0.0f, (float)m_InitParams.width, (float)m_InitParams.height);
|
|
|
|
|
gl.glDisablei(eGL_SCISSOR_TEST, 0);
|
|
|
|
|
gl.glPolygonMode(eGL_FRONT_AND_BACK, eGL_FILL);
|
|
|
|
|
|
|
|
|
|
if(gl.glClipControl && (GLCoreVersion >= 45 || ExtensionSupported[ExtensionSupported_ARB_clip_control])) // only available in 4.5+
|
|
|
|
|
gl.glClipControl(eGL_LOWER_LEFT, eGL_NEGATIVE_ONE_TO_ONE);
|
|
|
|
|
|
|
|
|
|
// bind UBOs
|
|
|
|
|
gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 0, ctxdata.GeneralUBO);
|
|
|
|
|
gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 1, ctxdata.GlyphUBO);
|
|
|
|
|
gl.glBindBufferBase(eGL_UNIFORM_BUFFER, 2, ctxdata.StringUBO);
|
|
|
|
|
|
|
|
|
|
// bind empty VAO just for valid rendering
|
|
|
|
|
gl.glBindVertexArray(ctxdata.DummyVAO);
|
|
|
|
|
|
|
|
|
|
// bind textures
|
|
|
|
|
gl.glActiveTexture(eGL_TEXTURE0);
|
|
|
|
|
gl.glBindTexture(eGL_TEXTURE_2D, ctxdata.GlyphTexture);
|
|
|
|
|
|
|
|
|
|
// bind program
|
|
|
|
|
gl.glUseProgram(ctxdata.Program);
|
|
|
|
|
|
|
|
|
|
// draw string
|
|
|
|
|
gl.glDrawArraysInstanced(eGL_TRIANGLE_STRIP, 0, 4, (GLsizei)len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct ReplacementSearch
|
|
|
|
@@ -1714,35 +1949,41 @@ void WrappedOpenGL::Present(void *windowHandle)
|
|
|
|
|
{
|
|
|
|
|
RenderTextState textState;
|
|
|
|
|
|
|
|
|
|
textState.Push(m_Real);
|
|
|
|
|
ContextData &ctxdata = GetCtxData();
|
|
|
|
|
|
|
|
|
|
textState.Push(m_Real, ctxdata.Modern());
|
|
|
|
|
|
|
|
|
|
// TODO: handle selecting active window amongst many
|
|
|
|
|
{
|
|
|
|
|
vector<KeyButton> keys = RenderDoc::Inst().GetCaptureKeys();
|
|
|
|
|
|
|
|
|
|
string overlayText = "OpenGL. ";
|
|
|
|
|
string overlayText = "OpenGL.";
|
|
|
|
|
|
|
|
|
|
for(size_t i=0; i < keys.size(); i++)
|
|
|
|
|
if(ctxdata.Modern())
|
|
|
|
|
{
|
|
|
|
|
if(i > 0)
|
|
|
|
|
overlayText += ", ";
|
|
|
|
|
overlayText += " ";
|
|
|
|
|
|
|
|
|
|
overlayText += ToStr::Get(keys[i]);
|
|
|
|
|
for(size_t i=0; i < keys.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
if(i > 0)
|
|
|
|
|
overlayText += ", ";
|
|
|
|
|
|
|
|
|
|
overlayText += ToStr::Get(keys[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!keys.empty())
|
|
|
|
|
overlayText += " to capture.";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!keys.empty())
|
|
|
|
|
overlayText += " to capture.";
|
|
|
|
|
|
|
|
|
|
if(overlay & eOverlay_FrameNumber)
|
|
|
|
|
{
|
|
|
|
|
if(!overlayText.empty()) overlayText += " ";
|
|
|
|
|
overlayText += StringFormat::Fmt("Frame: %d.", m_FrameCounter);
|
|
|
|
|
overlayText += StringFormat::Fmt(" Frame: %d.", m_FrameCounter);
|
|
|
|
|
}
|
|
|
|
|
if(overlay & eOverlay_FrameRate)
|
|
|
|
|
{
|
|
|
|
|
if(!overlayText.empty()) overlayText += " ";
|
|
|
|
|
overlayText += StringFormat::Fmt("%.2lf ms (%.2lf .. %.2lf) (%.0lf FPS)",
|
|
|
|
|
m_AvgFrametime, m_MinFrametime, m_MaxFrametime, 1000.0f/m_AvgFrametime);
|
|
|
|
|
overlayText += StringFormat::Fmt(" %.2lf ms (%.2lf .. %.2lf) (%.0lf FPS)",
|
|
|
|
|
m_AvgFrametime, m_MinFrametime, m_MaxFrametime,
|
|
|
|
|
m_AvgFrametime <= 0.0f ? 0.0f : 1000.0f/m_AvgFrametime);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float y=0.0f;
|
|
|
|
@@ -1753,7 +1994,23 @@ void WrappedOpenGL::Present(void *windowHandle)
|
|
|
|
|
y += 1.0f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(overlay & eOverlay_CaptureList)
|
|
|
|
|
if(ctxdata.Legacy())
|
|
|
|
|
{
|
|
|
|
|
if(!ctxdata.attribsCreate)
|
|
|
|
|
{
|
|
|
|
|
RenderOverlayText(0.0f, y, "Context not created via CreateContextAttribs. Capturing disabled.");
|
|
|
|
|
y += 1.0f;
|
|
|
|
|
}
|
|
|
|
|
RenderOverlayText(0.0f, y, "Only OpenGL 3.2+ contexts are supported.");
|
|
|
|
|
y += 1.0f;
|
|
|
|
|
}
|
|
|
|
|
else if(!ctxdata.isCore)
|
|
|
|
|
{
|
|
|
|
|
RenderOverlayText(0.0f, y, "WARNING: Non-core context in use. Compatibility profile not supported.");
|
|
|
|
|
y += 1.0f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(ctxdata.Modern() && (overlay & eOverlay_CaptureList))
|
|
|
|
|
{
|
|
|
|
|
RenderOverlayText(0.0f, y, "%d Captures saved.\n", (uint32_t)m_FrameRecord.size());
|
|
|
|
|
y += 1.0f;
|
|
|
|
@@ -1775,7 +2032,17 @@ void WrappedOpenGL::Present(void *windowHandle)
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
textState.Pop(m_Real);
|
|
|
|
|
textState.Pop(m_Real, ctxdata.Modern());
|
|
|
|
|
|
|
|
|
|
// swallow all errors we might have inadvertantly caused. This is
|
|
|
|
|
// better than letting an error propagate and maybe screw up the
|
|
|
|
|
// app (although it means we might swallow an error from before the
|
|
|
|
|
// SwapBuffers call, it can't be helped.
|
|
|
|
|
if(ctxdata.Legacy() && m_Real.glGetError)
|
|
|
|
|
{
|
|
|
|
|
GLenum err = m_Real.glGetError();
|
|
|
|
|
while(err) err = m_Real.glGetError();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -1936,7 +2203,8 @@ void WrappedOpenGL::Present(void *windowHandle)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(RenderDoc::Inst().ShouldTriggerCapture(m_FrameCounter) && m_State == WRITING_IDLE && m_FrameRecord.empty())
|
|
|
|
|
// only allow capturing on 'modern' created contexts
|
|
|
|
|
if(GetCtxData().Modern() && RenderDoc::Inst().ShouldTriggerCapture(m_FrameCounter) && m_State == WRITING_IDLE && m_FrameRecord.empty())
|
|
|
|
|
{
|
|
|
|
|
m_State = WRITING_CAPFRAME;
|
|
|
|
|
|
|
|
|
|