mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-07-08 08:40:55 +00:00
6d1d302491
* We enable a couple of high signal-to-noise warnings in all clang builds
2780 lines
86 KiB
C++
2780 lines
86 KiB
C++
/******************************************************************************
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2015-2019 Baldur Karlsson
|
|
* Copyright (c) 2014 Crytek
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
******************************************************************************/
|
|
|
|
#include "gl_common.h"
|
|
#include "core/core.h"
|
|
#include "strings/string_utils.h"
|
|
#include "gl_dispatch_table.h"
|
|
#include "gl_driver.h"
|
|
|
|
Threading::CriticalSection glLock;
|
|
|
|
GLDispatchTable GL = {};
|
|
|
|
GLChunk gl_CurChunk = GLChunk::Max;
|
|
|
|
bool HasExt[GLExtension_Count] = {};
|
|
bool VendorCheck[VendorCheck_Count] = {};
|
|
|
|
int GLCoreVersion = 0;
|
|
bool GLIsCore = false;
|
|
bool IsGLES = false;
|
|
|
|
template <>
|
|
bool CheckConstParam(bool t)
|
|
{
|
|
return t;
|
|
}
|
|
|
|
bool CheckReplayContext()
|
|
{
|
|
#define REQUIRE_FUNC(func) \
|
|
if(!GL.func) \
|
|
{ \
|
|
RDCERR("Missing core function " STRINGIZE(func)); \
|
|
return false; \
|
|
}
|
|
|
|
REQUIRE_FUNC(glGetString);
|
|
REQUIRE_FUNC(glGetStringi);
|
|
REQUIRE_FUNC(glGetIntegerv);
|
|
|
|
RDCLOG("Running GL replay on: %s / %s / %s", GL.glGetString(eGL_VENDOR),
|
|
GL.glGetString(eGL_RENDERER), GL.glGetString(eGL_VERSION));
|
|
|
|
std::string extensionString = "";
|
|
|
|
GLint numExts = 0;
|
|
GL.glGetIntegerv(eGL_NUM_EXTENSIONS, &numExts);
|
|
for(GLint e = 0; e < numExts; e++)
|
|
{
|
|
const char *ext = (const char *)GL.glGetStringi(eGL_EXTENSIONS, (GLuint)e);
|
|
|
|
extensionString += StringFormat::Fmt("[%d]: %s, ", e, ext);
|
|
|
|
if(e > 0 && (e % 25) == 0)
|
|
{
|
|
RDCLOG("%s", extensionString.c_str());
|
|
extensionString = "";
|
|
}
|
|
|
|
// skip the "GL_"
|
|
ext += 3;
|
|
}
|
|
|
|
if(!extensionString.empty())
|
|
RDCLOG("%s", extensionString.c_str());
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ValidateFunctionPointers()
|
|
{
|
|
PFNGLGETSTRINGPROC *ptrs = (PFNGLGETSTRINGPROC *)&GL;
|
|
size_t num = sizeof(GL) / sizeof(PFNGLGETSTRINGPROC);
|
|
|
|
RDCLOG("Function pointers available:");
|
|
for(size_t ptr = 0; ptr < num;)
|
|
{
|
|
uint64_t ptrmask = 0;
|
|
|
|
for(size_t j = 0; j < 64; j++)
|
|
if(ptr + j < num && ptrs[ptr + j])
|
|
ptrmask |= 1ULL << (63 - j);
|
|
|
|
ptr += 64;
|
|
|
|
RDCLOG("%064llb", ptrmask);
|
|
}
|
|
|
|
// check for the presence of GL functions we will call unconditionally as part of the replay
|
|
// process.
|
|
// Other functions that are only called to deserialise are checked for presence separately
|
|
|
|
bool ret = true;
|
|
|
|
#define CHECK_PRESENT(func) \
|
|
if(!GL.func) \
|
|
{ \
|
|
RDCERR( \
|
|
"Missing function %s, required for replay. RenderDoc requires a 3.2 context, " \
|
|
"and a handful of extensions, see the Documentation.", \
|
|
STRINGIZE(func)); \
|
|
ret = false; \
|
|
}
|
|
|
|
// these functions should all be present as part of a 3.2 context plus the extensions we require,
|
|
// but let's just be extra-careful
|
|
|
|
// both GL and GLES, some of them are emulated
|
|
CHECK_PRESENT(glActiveTexture)
|
|
CHECK_PRESENT(glAttachShader)
|
|
CHECK_PRESENT(glBeginQuery)
|
|
CHECK_PRESENT(glBindAttribLocation)
|
|
CHECK_PRESENT(glBindBuffer)
|
|
CHECK_PRESENT(glBindBufferBase)
|
|
CHECK_PRESENT(glBindBufferRange)
|
|
CHECK_PRESENT(glBindFramebuffer)
|
|
CHECK_PRESENT(glBindProgramPipeline)
|
|
CHECK_PRESENT(glBindSampler)
|
|
CHECK_PRESENT(glBindTexture)
|
|
CHECK_PRESENT(glBindVertexArray)
|
|
CHECK_PRESENT(glBindVertexBuffer)
|
|
CHECK_PRESENT(glBlendColor)
|
|
CHECK_PRESENT(glBlendEquationSeparate)
|
|
CHECK_PRESENT(glBlendFunc)
|
|
CHECK_PRESENT(glBlendFuncSeparate)
|
|
CHECK_PRESENT(glBlitFramebuffer)
|
|
CHECK_PRESENT(glBufferData)
|
|
CHECK_PRESENT(glBufferSubData)
|
|
CHECK_PRESENT(glClearBufferData)
|
|
CHECK_PRESENT(glClearBufferfi)
|
|
CHECK_PRESENT(glClearBufferfv)
|
|
CHECK_PRESENT(glClearBufferiv)
|
|
CHECK_PRESENT(glClearBufferuiv)
|
|
CHECK_PRESENT(glClearColor)
|
|
CHECK_PRESENT(glClearDepthf)
|
|
CHECK_PRESENT(glCompileShader)
|
|
CHECK_PRESENT(glCopyImageSubData)
|
|
CHECK_PRESENT(glCreateProgram)
|
|
CHECK_PRESENT(glCreateShader)
|
|
CHECK_PRESENT(glCreateShaderProgramv)
|
|
CHECK_PRESENT(glCullFace)
|
|
CHECK_PRESENT(glDeleteBuffers)
|
|
CHECK_PRESENT(glDeleteFramebuffers)
|
|
CHECK_PRESENT(glDeleteProgram)
|
|
CHECK_PRESENT(glDeleteProgramPipelines)
|
|
CHECK_PRESENT(glDeleteQueries)
|
|
CHECK_PRESENT(glDeleteSamplers)
|
|
CHECK_PRESENT(glDeleteShader)
|
|
CHECK_PRESENT(glDeleteTextures)
|
|
CHECK_PRESENT(glDeleteVertexArrays)
|
|
CHECK_PRESENT(glDepthFunc)
|
|
CHECK_PRESENT(glDepthMask)
|
|
CHECK_PRESENT(glDetachShader)
|
|
CHECK_PRESENT(glDisable)
|
|
CHECK_PRESENT(glDisableVertexAttribArray)
|
|
CHECK_PRESENT(glDrawArrays)
|
|
CHECK_PRESENT(glDrawArraysInstanced)
|
|
CHECK_PRESENT(glDrawBuffers)
|
|
CHECK_PRESENT(glDrawElements)
|
|
CHECK_PRESENT(glDrawElementsBaseVertex)
|
|
CHECK_PRESENT(glEnable)
|
|
CHECK_PRESENT(glEnableVertexAttribArray)
|
|
CHECK_PRESENT(glEndQuery)
|
|
CHECK_PRESENT(glFramebufferTexture2D)
|
|
CHECK_PRESENT(glFramebufferTextureLayer)
|
|
CHECK_PRESENT(glFrontFace)
|
|
CHECK_PRESENT(glGenBuffers)
|
|
CHECK_PRESENT(glGenFramebuffers)
|
|
CHECK_PRESENT(glGenProgramPipelines)
|
|
CHECK_PRESENT(glGenQueries)
|
|
CHECK_PRESENT(glGenSamplers)
|
|
CHECK_PRESENT(glGenTextures)
|
|
CHECK_PRESENT(glGenVertexArrays)
|
|
CHECK_PRESENT(glGetActiveUniformBlockiv)
|
|
CHECK_PRESENT(glGetAttribLocation)
|
|
CHECK_PRESENT(glGetBooleani_v)
|
|
CHECK_PRESENT(glGetBooleanv)
|
|
CHECK_PRESENT(glGetBufferParameteriv)
|
|
CHECK_PRESENT(glGetBufferSubData)
|
|
CHECK_PRESENT(glGetError)
|
|
CHECK_PRESENT(glGetFloatv)
|
|
CHECK_PRESENT(glGetFragDataLocation)
|
|
CHECK_PRESENT(glGetFramebufferAttachmentParameteriv)
|
|
CHECK_PRESENT(glGetInteger64i_v)
|
|
CHECK_PRESENT(glGetIntegeri_v)
|
|
CHECK_PRESENT(glGetIntegerv)
|
|
CHECK_PRESENT(glGetInternalformativ)
|
|
CHECK_PRESENT(glGetProgramInfoLog)
|
|
CHECK_PRESENT(glGetProgramInterfaceiv)
|
|
CHECK_PRESENT(glGetProgramiv)
|
|
CHECK_PRESENT(glGetProgramPipelineiv)
|
|
CHECK_PRESENT(glGetProgramResourceIndex)
|
|
CHECK_PRESENT(glGetProgramResourceiv)
|
|
CHECK_PRESENT(glGetProgramResourceName)
|
|
CHECK_PRESENT(glGetQueryObjectuiv)
|
|
CHECK_PRESENT(glGetSamplerParameterfv)
|
|
CHECK_PRESENT(glGetSamplerParameteriv)
|
|
CHECK_PRESENT(glGetShaderInfoLog)
|
|
CHECK_PRESENT(glGetShaderiv)
|
|
CHECK_PRESENT(glGetString)
|
|
CHECK_PRESENT(glGetStringi)
|
|
CHECK_PRESENT(glGetTexImage)
|
|
CHECK_PRESENT(glGetTexLevelParameteriv)
|
|
CHECK_PRESENT(glGetTexParameterfv)
|
|
CHECK_PRESENT(glGetTexParameteriv)
|
|
CHECK_PRESENT(glGetUniformBlockIndex)
|
|
CHECK_PRESENT(glGetUniformfv)
|
|
CHECK_PRESENT(glGetUniformiv)
|
|
CHECK_PRESENT(glGetUniformLocation)
|
|
CHECK_PRESENT(glGetUniformuiv)
|
|
CHECK_PRESENT(glGetVertexAttribfv)
|
|
CHECK_PRESENT(glGetVertexAttribiv)
|
|
CHECK_PRESENT(glHint)
|
|
CHECK_PRESENT(glIsEnabled)
|
|
CHECK_PRESENT(glLineWidth)
|
|
CHECK_PRESENT(glLinkProgram)
|
|
CHECK_PRESENT(glMapBufferRange)
|
|
CHECK_PRESENT(glPixelStorei)
|
|
CHECK_PRESENT(glPolygonOffset)
|
|
CHECK_PRESENT(glProgramParameteri)
|
|
CHECK_PRESENT(glProgramUniform1fv)
|
|
CHECK_PRESENT(glProgramUniform1iv)
|
|
CHECK_PRESENT(glProgramUniform1ui)
|
|
CHECK_PRESENT(glProgramUniform1uiv)
|
|
CHECK_PRESENT(glProgramUniform2fv)
|
|
CHECK_PRESENT(glProgramUniform2iv)
|
|
CHECK_PRESENT(glProgramUniform2uiv)
|
|
CHECK_PRESENT(glProgramUniform3fv)
|
|
CHECK_PRESENT(glProgramUniform3iv)
|
|
CHECK_PRESENT(glProgramUniform3uiv)
|
|
CHECK_PRESENT(glProgramUniform4fv)
|
|
CHECK_PRESENT(glProgramUniform4iv)
|
|
CHECK_PRESENT(glProgramUniform4ui)
|
|
CHECK_PRESENT(glProgramUniform4uiv)
|
|
CHECK_PRESENT(glProgramUniformMatrix2fv)
|
|
CHECK_PRESENT(glProgramUniformMatrix2x3fv)
|
|
CHECK_PRESENT(glProgramUniformMatrix2x4fv)
|
|
CHECK_PRESENT(glProgramUniformMatrix3fv)
|
|
CHECK_PRESENT(glProgramUniformMatrix3x2fv)
|
|
CHECK_PRESENT(glProgramUniformMatrix3x4fv)
|
|
CHECK_PRESENT(glProgramUniformMatrix4fv)
|
|
CHECK_PRESENT(glProgramUniformMatrix4x2fv)
|
|
CHECK_PRESENT(glProgramUniformMatrix4x3fv)
|
|
CHECK_PRESENT(glReadBuffer)
|
|
CHECK_PRESENT(glReadPixels)
|
|
CHECK_PRESENT(glSampleCoverage)
|
|
CHECK_PRESENT(glSampleMaski)
|
|
CHECK_PRESENT(glSamplerParameteri)
|
|
CHECK_PRESENT(glShaderSource)
|
|
CHECK_PRESENT(glStencilFuncSeparate)
|
|
CHECK_PRESENT(glStencilMask)
|
|
CHECK_PRESENT(glStencilMaskSeparate)
|
|
CHECK_PRESENT(glStencilOpSeparate)
|
|
CHECK_PRESENT(glTexImage2D)
|
|
CHECK_PRESENT(glTexParameteri)
|
|
CHECK_PRESENT(glUniform1i)
|
|
CHECK_PRESENT(glUniform1ui)
|
|
CHECK_PRESENT(glUniform2f)
|
|
CHECK_PRESENT(glUniform2fv)
|
|
CHECK_PRESENT(glUniform4fv)
|
|
CHECK_PRESENT(glUniformBlockBinding)
|
|
CHECK_PRESENT(glUniformMatrix4fv)
|
|
CHECK_PRESENT(glUnmapBuffer)
|
|
CHECK_PRESENT(glUseProgram)
|
|
CHECK_PRESENT(glUseProgramStages)
|
|
CHECK_PRESENT(glVertexAttrib4fv)
|
|
CHECK_PRESENT(glVertexAttribBinding)
|
|
CHECK_PRESENT(glVertexAttribFormat)
|
|
CHECK_PRESENT(glVertexAttribIFormat)
|
|
CHECK_PRESENT(glVertexAttribPointer)
|
|
CHECK_PRESENT(glVertexBindingDivisor)
|
|
CHECK_PRESENT(glViewport)
|
|
|
|
// GL only
|
|
if(!IsGLES)
|
|
{
|
|
CHECK_PRESENT(glBindFragDataLocation)
|
|
CHECK_PRESENT(glEndConditionalRender)
|
|
CHECK_PRESENT(glFramebufferTexture3D)
|
|
CHECK_PRESENT(glGetCompressedTexImage)
|
|
CHECK_PRESENT(glGetDoublev)
|
|
CHECK_PRESENT(glGetUniformdv)
|
|
CHECK_PRESENT(glLogicOp)
|
|
CHECK_PRESENT(glPointParameterf)
|
|
CHECK_PRESENT(glPointParameteri)
|
|
CHECK_PRESENT(glPointSize)
|
|
CHECK_PRESENT(glPolygonMode)
|
|
CHECK_PRESENT(glPrimitiveRestartIndex)
|
|
CHECK_PRESENT(glProgramUniform1dv)
|
|
CHECK_PRESENT(glProgramUniform2dv)
|
|
CHECK_PRESENT(glProgramUniform3dv)
|
|
CHECK_PRESENT(glProgramUniform4dv)
|
|
CHECK_PRESENT(glProgramUniformMatrix2dv)
|
|
CHECK_PRESENT(glProgramUniformMatrix2x3dv)
|
|
CHECK_PRESENT(glProgramUniformMatrix2x4dv)
|
|
CHECK_PRESENT(glProgramUniformMatrix3dv)
|
|
CHECK_PRESENT(glProgramUniformMatrix3x2dv)
|
|
CHECK_PRESENT(glProgramUniformMatrix3x4dv)
|
|
CHECK_PRESENT(glProgramUniformMatrix4dv)
|
|
CHECK_PRESENT(glProgramUniformMatrix4x2dv)
|
|
CHECK_PRESENT(glProgramUniformMatrix4x3dv)
|
|
CHECK_PRESENT(glProvokingVertex)
|
|
CHECK_PRESENT(glVertexAttribLFormat)
|
|
}
|
|
|
|
// other functions are either checked for presence explicitly (like
|
|
// depth bounds or polygon offset clamp EXT functions), or they are
|
|
// only called when such a call is serialised from the logfile, and
|
|
// so they are checked for validity separately.
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void CheckExtFromString(const char *ext)
|
|
{
|
|
if(ext == NULL || !ext[0] || !ext[1] || !ext[2] || !ext[3])
|
|
return;
|
|
|
|
ext += 3;
|
|
|
|
#undef EXT_TO_CHECK
|
|
#define EXT_TO_CHECK(ver, glesver, extname) \
|
|
if((!IsGLES && GLCoreVersion >= ver) || (IsGLES && GLCoreVersion >= glesver) || \
|
|
!strcmp(ext, STRINGIZE(extname))) \
|
|
HasExt[extname] = true;
|
|
|
|
EXTENSION_CHECKS()
|
|
|
|
if(IsGLES)
|
|
{
|
|
#define EXT_COMP_CHECK(extname, glesextname) \
|
|
if(!strcmp(ext, STRINGIZE(glesextname))) \
|
|
HasExt[extname] = true;
|
|
|
|
EXTENSION_COMPATIBILITY_CHECKS()
|
|
}
|
|
}
|
|
|
|
void GetContextVersion(bool &ctxGLES, int &ctxVersion)
|
|
{
|
|
if(GL.glGetString)
|
|
{
|
|
const char *version = (const char *)GL.glGetString(eGL_VERSION);
|
|
|
|
// check whether we are using OpenGL ES
|
|
// GL_VERSION for OpenGL ES:
|
|
// "OpenGL ES N.M vendor-specific information"
|
|
// for 1.x:
|
|
// "OpenGL ES-XX N.M vendor-specific information"
|
|
if(!strncmp(version, "OpenGL ES-", 10))
|
|
{
|
|
ctxGLES = true;
|
|
|
|
// assume 1.0, doesn't matter if it's 1.1
|
|
ctxVersion = 10;
|
|
}
|
|
else if(!strncmp(version, "OpenGL ES", 9))
|
|
{
|
|
ctxGLES = true;
|
|
|
|
int mj = int(version[10] - '0');
|
|
int mn = int(version[12] - '0');
|
|
ctxVersion = mj * 10 + mn;
|
|
}
|
|
else
|
|
{
|
|
ctxGLES = false;
|
|
|
|
int mj = int(version[0] - '0');
|
|
int mn = int(version[1] - '0');
|
|
ctxVersion = mj * 10 + mn;
|
|
}
|
|
}
|
|
|
|
if(GL.glGetIntegerv)
|
|
{
|
|
GLint mj = 0, mn = 0;
|
|
GL.glGetIntegerv(eGL_MAJOR_VERSION, &mj);
|
|
GL.glGetIntegerv(eGL_MINOR_VERSION, &mn);
|
|
|
|
if(mj > 0)
|
|
ctxVersion = mj * 10 + mn;
|
|
}
|
|
}
|
|
|
|
void FetchEnabledExtensions()
|
|
{
|
|
RDCEraseEl(HasExt);
|
|
|
|
int ctxVersion = 0;
|
|
bool ctxGLES = false;
|
|
GetContextVersion(ctxGLES, ctxVersion);
|
|
|
|
GLCoreVersion = RDCMAX(GLCoreVersion, ctxVersion);
|
|
IsGLES = ctxGLES;
|
|
|
|
RDCLOG("Checking enabled extensions, running as %s %d.%d", IsGLES ? "OpenGL ES" : "OpenGL",
|
|
(ctxVersion / 10), (ctxVersion % 10));
|
|
|
|
// only use glGetStringi on 3.0 contexts and above (ES and GL), even if we have the function
|
|
// pointer
|
|
if(GL.glGetStringi && ctxVersion >= 30)
|
|
{
|
|
GLint numExts = 0;
|
|
if(GL.glGetIntegerv)
|
|
GL.glGetIntegerv(eGL_NUM_EXTENSIONS, &numExts);
|
|
|
|
for(int i = 0; i < numExts; i++)
|
|
{
|
|
const char *ext = (const char *)GL.glGetStringi(eGL_EXTENSIONS, (GLuint)i);
|
|
|
|
CheckExtFromString(ext);
|
|
}
|
|
}
|
|
else if(GL.glGetString)
|
|
{
|
|
std::string extstr = (const char *)GL.glGetString(eGL_EXTENSIONS);
|
|
|
|
std::vector<std::string> extlist;
|
|
split(extstr, extlist, ' ');
|
|
|
|
for(const std::string &e : extlist)
|
|
CheckExtFromString(e.c_str());
|
|
}
|
|
|
|
if(!HasExt[ARB_separate_shader_objects])
|
|
{
|
|
if(HasExt[ARB_program_interface_query])
|
|
RDCWARN(
|
|
"Because ARB_separate_shader_objects is not supported, forcibly disabling "
|
|
"ARB_program_interface_query");
|
|
|
|
HasExt[ARB_program_interface_query] = false;
|
|
}
|
|
|
|
if(!IsGLES && GLCoreVersion < 42 && HasExt[ARB_compute_shader])
|
|
{
|
|
RDCERR("GL implementation has ARB_compute_shader but is not at least 4.2. Disabling compute.");
|
|
HasExt[ARB_compute_shader] = false;
|
|
}
|
|
}
|
|
|
|
void DoVendorChecks(GLPlatform &platform, GLWindowingData context)
|
|
{
|
|
const char *vendor = "";
|
|
const char *renderer = "";
|
|
const char *version = "";
|
|
|
|
if(GL.glGetString)
|
|
{
|
|
vendor = (const char *)GL.glGetString(eGL_VENDOR);
|
|
renderer = (const char *)GL.glGetString(eGL_RENDERER);
|
|
version = (const char *)GL.glGetString(eGL_VERSION);
|
|
|
|
RDCLOG("Vendor checks for %u (%s / %s / %s)", GLCoreVersion, vendor, renderer, version);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////
|
|
// version/driver/vendor specific hacks and checks go here
|
|
// doing these in a central place means they're all documented and
|
|
// can be removed ASAP from a single place.
|
|
// It also means any work done to figure them out is only ever done
|
|
// in one place, when first activating a new context, so hopefully
|
|
// shouldn't interfere with the running program
|
|
|
|
// The linux AMD driver doesn't recognise GL_VERTEX_BINDING_BUFFER.
|
|
// However it has a "two wrongs make a right" type deal. Instead of returning the buffer that the
|
|
// i'th index is bound to (as above, vbslot) for GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, it returns
|
|
// the i'th
|
|
// vertex buffer which is exactly what we wanted from GL_VERTEX_BINDING_BUFFER!
|
|
// see: http://devgurus.amd.com/message/1306745#1306745
|
|
|
|
RDCEraseEl(VendorCheck);
|
|
|
|
if(GL.glGetError && GL.glGetIntegeri_v && HasExt[ARB_vertex_attrib_binding])
|
|
{
|
|
// clear all error flags.
|
|
GLenum err = eGL_NONE;
|
|
ClearGLErrors();
|
|
|
|
GLint dummy = 0;
|
|
GL.glGetIntegeri_v(eGL_VERTEX_BINDING_BUFFER, 0, &dummy);
|
|
err = GL.glGetError();
|
|
|
|
if(err != eGL_NONE)
|
|
{
|
|
// if we got an error trying to query that, we should enable this hack
|
|
VendorCheck[VendorCheck_AMD_vertex_buffer_query] = true;
|
|
|
|
RDCWARN("Using AMD hack to avoid GL_VERTEX_BINDING_BUFFER");
|
|
}
|
|
}
|
|
|
|
if(GL.glGetIntegerv && GL.glGetError && !IsGLES)
|
|
{
|
|
// NOTE: in case of OpenGL ES the GL_NV_polygon_mode extension can be used, however even if the
|
|
// driver reports that the extension is supported, it always throws errors when we try to use it
|
|
// (at least with the current NVIDIA driver)
|
|
|
|
// clear all error flags.
|
|
GLenum err = eGL_NONE;
|
|
ClearGLErrors();
|
|
|
|
GLint dummy[2] = {0};
|
|
GL.glGetIntegerv(eGL_POLYGON_MODE, dummy);
|
|
err = GL.glGetError();
|
|
|
|
if(err != eGL_NONE)
|
|
{
|
|
// if we got an error trying to query that, we should enable this hack
|
|
VendorCheck[VendorCheck_AMD_polygon_mode_query] = true;
|
|
|
|
RDCWARN("Using AMD hack to avoid GL_POLYGON_MODE");
|
|
}
|
|
}
|
|
|
|
// AMD throws an error if we try to copy the mips that are smaller than 4x4.
|
|
//
|
|
// Intel seems to completely break everything if we even run this check, so we just
|
|
// skip this check and assume the hack is enabled.
|
|
|
|
if(!strcmp(vendor, "Intel") || !strcmp(vendor, "intel") || !strcmp(vendor, "INTEL"))
|
|
{
|
|
RDCWARN("Using super hack-on-a-hack to avoid glCopyImageSubData tests on intel.");
|
|
VendorCheck[VendorCheck_AMD_copy_compressed_tinymips] = true;
|
|
VendorCheck[VendorCheck_AMD_copy_compressed_cubemaps] = true;
|
|
}
|
|
else if(GL.glGetError && GL.glGenTextures && GL.glBindTexture && GL.glCopyImageSubData &&
|
|
GL.glTexStorage2D && GL.glTexSubImage2D && GL.glTexParameteri && GL.glDeleteTextures &&
|
|
HasExt[ARB_copy_image] && HasExt[ARB_texture_storage] && !IsGLES)
|
|
{
|
|
GLuint prevTex = 0;
|
|
GL.glGetIntegerv(eGL_TEXTURE_BINDING_2D, (GLint *)&prevTex);
|
|
|
|
GLuint texs[2];
|
|
GL.glGenTextures(2, texs);
|
|
|
|
GL.glBindTexture(eGL_TEXTURE_2D, texs[0]);
|
|
GL.glTexStorage2D(eGL_TEXTURE_2D, 1, eGL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 1, 1);
|
|
GL.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MAX_LEVEL, 0);
|
|
|
|
GL.glBindTexture(eGL_TEXTURE_2D, texs[1]);
|
|
GL.glTexStorage2D(eGL_TEXTURE_2D, 1, eGL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 1, 1);
|
|
GL.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MAX_LEVEL, 0);
|
|
|
|
// clear all error flags.
|
|
GLenum err = eGL_NONE;
|
|
ClearGLErrors();
|
|
|
|
GL.glCopyImageSubData(texs[0], eGL_TEXTURE_2D, 0, 0, 0, 0, texs[1], eGL_TEXTURE_2D, 0, 0, 0, 0,
|
|
1, 1, 1);
|
|
|
|
err = GL.glGetError();
|
|
|
|
if(err != eGL_NONE)
|
|
{
|
|
// if we got an error trying to query that, we should enable this hack
|
|
VendorCheck[VendorCheck_AMD_copy_compressed_tinymips] = true;
|
|
|
|
RDCWARN("Using hack to avoid glCopyImageSubData on lowest mips of compressed texture");
|
|
}
|
|
|
|
GL.glBindTexture(eGL_TEXTURE_2D, prevTex);
|
|
GL.glDeleteTextures(2, texs);
|
|
|
|
ClearGLErrors();
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Check copying cubemaps
|
|
|
|
GL.glGetIntegerv(eGL_TEXTURE_BINDING_CUBE_MAP, (GLint *)&prevTex);
|
|
GL.glGenTextures(2, texs);
|
|
|
|
const size_t dim = 32;
|
|
|
|
char buf[dim * dim / 2];
|
|
|
|
GL.glBindTexture(eGL_TEXTURE_CUBE_MAP, texs[0]);
|
|
GL.glTexStorage2D(eGL_TEXTURE_CUBE_MAP, 1, eGL_COMPRESSED_RGBA_S3TC_DXT1_EXT, dim, dim);
|
|
GL.glTexParameteri(eGL_TEXTURE_CUBE_MAP, eGL_TEXTURE_MAX_LEVEL, 0);
|
|
|
|
for(int i = 0; i < 6; i++)
|
|
{
|
|
memset(buf, 0xba + i, sizeof(buf));
|
|
GL.glCompressedTexSubImage2D(GLenum(eGL_TEXTURE_CUBE_MAP_POSITIVE_X + i), 0, 0, 0, dim, dim,
|
|
eGL_COMPRESSED_RGBA_S3TC_DXT1_EXT, dim * dim / 2, buf);
|
|
}
|
|
|
|
GL.glBindTexture(eGL_TEXTURE_CUBE_MAP, texs[1]);
|
|
GL.glTexStorage2D(eGL_TEXTURE_CUBE_MAP, 1, eGL_COMPRESSED_RGBA_S3TC_DXT1_EXT, dim, dim);
|
|
GL.glTexParameteri(eGL_TEXTURE_CUBE_MAP, eGL_TEXTURE_MAX_LEVEL, 0);
|
|
|
|
GL.glCopyImageSubData(texs[0], eGL_TEXTURE_CUBE_MAP, 0, 0, 0, 0, texs[1], eGL_TEXTURE_CUBE_MAP,
|
|
0, 0, 0, 0, dim, dim, 6);
|
|
|
|
char cmp[dim * dim / 2];
|
|
|
|
GL.glBindTexture(eGL_TEXTURE_CUBE_MAP, texs[0]);
|
|
|
|
for(int i = 0; i < 6; i++)
|
|
{
|
|
memset(buf, 0xba + i, sizeof(buf));
|
|
RDCEraseEl(cmp);
|
|
GL.glGetCompressedTexImage(GLenum(eGL_TEXTURE_CUBE_MAP_POSITIVE_X + i), 0, cmp);
|
|
|
|
RDCCOMPILE_ASSERT(sizeof(buf) == sizeof(buf), "Buffers are not matching sizes");
|
|
|
|
if(memcmp(buf, cmp, sizeof(buf)))
|
|
{
|
|
RDCERR("glGetTexImage from the source texture returns incorrect data!");
|
|
VendorCheck[VendorCheck_AMD_copy_compressed_cubemaps] =
|
|
true; // to be safe, enable the hack
|
|
}
|
|
}
|
|
|
|
GL.glBindTexture(eGL_TEXTURE_CUBE_MAP, texs[1]);
|
|
|
|
for(int i = 0; i < 6; i++)
|
|
{
|
|
memset(buf, 0xba + i, sizeof(buf));
|
|
RDCEraseEl(cmp);
|
|
GL.glGetCompressedTexImage(GLenum(eGL_TEXTURE_CUBE_MAP_POSITIVE_X + i), 0, cmp);
|
|
|
|
RDCCOMPILE_ASSERT(sizeof(buf) == sizeof(buf), "Buffers are not matching sizes");
|
|
|
|
if(memcmp(buf, cmp, sizeof(buf)))
|
|
{
|
|
RDCWARN("Using hack to avoid glCopyImageSubData on cubemap textures");
|
|
VendorCheck[VendorCheck_AMD_copy_compressed_cubemaps] = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
GL.glBindTexture(eGL_TEXTURE_CUBE_MAP, prevTex);
|
|
GL.glDeleteTextures(2, texs);
|
|
|
|
ClearGLErrors();
|
|
}
|
|
|
|
if(GL.glGetError && GL.glGenProgramPipelines && GL.glDeleteProgramPipelines &&
|
|
GL.glGetProgramPipelineiv && HasExt[ARB_compute_shader] && HasExt[ARB_program_interface_query])
|
|
{
|
|
GLuint pipe = 0;
|
|
GL.glGenProgramPipelines(1, &pipe);
|
|
|
|
// clear all error flags.
|
|
GLenum err = eGL_NONE;
|
|
ClearGLErrors();
|
|
|
|
GLint dummy = 0;
|
|
GL.glGetProgramPipelineiv(pipe, eGL_COMPUTE_SHADER, &dummy);
|
|
|
|
err = GL.glGetError();
|
|
|
|
if(err != eGL_NONE)
|
|
{
|
|
// if we got an error trying to query that, we should enable this hack
|
|
VendorCheck[VendorCheck_AMD_pipeline_compute_query] = true;
|
|
|
|
RDCWARN("Using hack to avoid glGetProgramPipelineiv with GL_COMPUTE_SHADER");
|
|
}
|
|
|
|
GL.glDeleteProgramPipelines(1, &pipe);
|
|
}
|
|
|
|
// only do this when we have a proper context e.g. on windows where an old
|
|
// context is first created. Check to see if FBOs or VAOs are shared between
|
|
// contexts.
|
|
if((IsGLES || GLCoreVersion >= 32) && GL.glGenVertexArrays && GL.glBindVertexArray &&
|
|
GL.glDeleteVertexArrays && GL.glGenFramebuffers && GL.glBindFramebuffer &&
|
|
GL.glDeleteFramebuffers)
|
|
{
|
|
GLuint prevFBO = 0, prevVAO = 0;
|
|
GL.glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, (GLint *)&prevFBO);
|
|
GL.glGetIntegerv(eGL_VERTEX_ARRAY_BINDING, (GLint *)&prevVAO);
|
|
|
|
// gen & create an FBO and VAO
|
|
GLuint fbo = 0;
|
|
GLuint vao = 0;
|
|
GL.glGenFramebuffers(1, &fbo);
|
|
GL.glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, fbo);
|
|
GL.glGenVertexArrays(1, &vao);
|
|
GL.glBindVertexArray(vao);
|
|
|
|
// make a context that shares with the current one, and switch to it
|
|
GLWindowingData child = platform.CloneTemporaryContext(context);
|
|
|
|
if(child.ctx)
|
|
{
|
|
// switch to child
|
|
platform.MakeContextCurrent(child);
|
|
|
|
// these shouldn't be visible
|
|
VendorCheck[VendorCheck_EXT_fbo_shared] = (GL.glIsFramebuffer(fbo) != GL_FALSE);
|
|
VendorCheck[VendorCheck_EXT_vao_shared] = (GL.glIsVertexArray(vao) != GL_FALSE);
|
|
|
|
if(VendorCheck[VendorCheck_EXT_fbo_shared])
|
|
RDCWARN("FBOs are shared on this implementation");
|
|
if(VendorCheck[VendorCheck_EXT_vao_shared])
|
|
RDCWARN("VAOs are shared on this implementation");
|
|
|
|
// switch back to context
|
|
platform.MakeContextCurrent(context);
|
|
|
|
platform.DeleteClonedContext(child);
|
|
}
|
|
|
|
GL.glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, prevFBO);
|
|
GL.glBindVertexArray(prevVAO);
|
|
|
|
GL.glDeleteFramebuffers(1, &fbo);
|
|
GL.glDeleteVertexArrays(1, &vao);
|
|
}
|
|
|
|
// don't have a test for this, just have to enable it all the time, for now.
|
|
VendorCheck[VendorCheck_NV_avoid_D32S8_copy] = false;
|
|
|
|
// On 32-bit calling this function could actually lead to crashes (issues with
|
|
// esp being saved across the call), so since the work-around is low-cost of just
|
|
// emulating that function we just always enable it.
|
|
//
|
|
// NOTE: Vendor Checks are initialised after the function pointers will be set up
|
|
// so we have to do this unconditionally, this value isn't checked anywhere.
|
|
// Search for where this is applied in gl_emulated.cpp
|
|
//
|
|
// Update 2018-Jan - this might be the problem with the registry having the wrong signature for
|
|
// glClearNamedFramebufferfi - if the arguments were mismatched it would explain both invalid
|
|
// argument errors and ABI problems. For now though (and since as mentioned above it's cheap to
|
|
// emulate) we leave it on. See issue #842
|
|
VendorCheck[VendorCheck_NV_ClearNamedFramebufferfiBugs] = true;
|
|
|
|
// glVertexArrayElementBuffer doesn't update the GL_ELEMENT_ARRAY_BUFFER_BINDING global query,
|
|
// when binding the VAO subsequently *will*.
|
|
// I'm not sure if that's correct (weird) behaviour or buggy, but we can work around it just by
|
|
// avoiding use of the DSA function and always doing our emulated version.
|
|
VendorCheck[VendorCheck_AMD_vertex_array_elem_buffer_query] = true;
|
|
|
|
// Qualcomm's implementation of glCopyImageSubData is buggy on some drivers and can cause GPU
|
|
// crashes or corrupted data. We force the initial state copies to happen via our emulation which
|
|
// uses framebuffer blits.
|
|
if(strstr(vendor, "Qualcomm") || strstr(vendor, "Adreno") || strstr(renderer, "Qualcomm") ||
|
|
strstr(renderer, "Adreno"))
|
|
{
|
|
bool broken = true;
|
|
|
|
// the bug should be fixed in version 325 and above
|
|
const char *qualcommver = strstr(version, "V@");
|
|
uint32_t ver = 0;
|
|
|
|
if(qualcommver)
|
|
{
|
|
qualcommver += 2;
|
|
|
|
while(*qualcommver >= '0' && *qualcommver <= '9')
|
|
{
|
|
ver *= 10;
|
|
ver += int((*qualcommver) - '0');
|
|
qualcommver++;
|
|
}
|
|
|
|
if(ver >= 325)
|
|
broken = false;
|
|
}
|
|
|
|
if(broken)
|
|
{
|
|
RDCWARN("Detected Qualcomm driver version %u, Using hack to avoid glCopyImageSubData", ver);
|
|
VendorCheck[VendorCheck_Qualcomm_avoid_glCopyImageSubData] = true;
|
|
}
|
|
}
|
|
|
|
if(IsGLES)
|
|
{
|
|
// Check whether reading from the depth, stencil and depth-stencil buffers using glReadPixels is
|
|
// supported or not.
|
|
if(!HasExt[NV_read_depth])
|
|
RDCWARN(
|
|
"Reading from the depth buffer using glReadPixels is not supported (GL_NV_read_depth)");
|
|
if(!HasExt[NV_read_stencil])
|
|
RDCWARN(
|
|
"Reading from the stencil buffer using glReadPixels is not supported "
|
|
"(GL_NV_read_stencil)");
|
|
if(!HasExt[NV_read_depth_stencil])
|
|
RDCWARN(
|
|
"Reading from the packed depth-stencil buffers using glReadPixels is not supported "
|
|
"(GL_NV_read_depth_stencil)");
|
|
}
|
|
}
|
|
|
|
GLMarkerRegion::GLMarkerRegion(const std::string &marker, GLenum source, GLuint id)
|
|
{
|
|
Begin(marker, source, id);
|
|
}
|
|
|
|
GLMarkerRegion::~GLMarkerRegion()
|
|
{
|
|
End();
|
|
}
|
|
|
|
void GLMarkerRegion::Begin(const std::string &marker, GLenum source, GLuint id)
|
|
{
|
|
if(!HasExt[KHR_debug] || !GL.glPushDebugGroup)
|
|
return;
|
|
|
|
GL.glPushDebugGroup(source, id, -1, marker.c_str());
|
|
}
|
|
|
|
void GLMarkerRegion::Set(const std::string &marker, GLenum source, GLuint id, GLenum severity)
|
|
{
|
|
if(!HasExt[KHR_debug] || !GL.glDebugMessageInsert)
|
|
return;
|
|
|
|
GL.glDebugMessageInsert(source, eGL_DEBUG_TYPE_MARKER, id, severity, -1, marker.c_str());
|
|
}
|
|
|
|
void GLMarkerRegion::End()
|
|
{
|
|
if(!HasExt[KHR_debug] || !GL.glPopDebugGroup)
|
|
return;
|
|
|
|
GL.glPopDebugGroup();
|
|
}
|
|
|
|
void GLPushPopState::Push(bool modern)
|
|
{
|
|
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)
|
|
{
|
|
if(!IsGLES)
|
|
enableBits[3] = GL.glIsEnabled(eGL_DEPTH_CLAMP) != 0;
|
|
|
|
if(HasExt[ARB_draw_buffers_blend])
|
|
enableBits[4] = GL.glIsEnabledi(eGL_BLEND, 0) != 0;
|
|
else
|
|
enableBits[4] = GL.glIsEnabled(eGL_BLEND) != 0;
|
|
|
|
if(HasExt[ARB_viewport_array])
|
|
enableBits[5] = GL.glIsEnabledi(eGL_SCISSOR_TEST, 0) != 0;
|
|
else
|
|
enableBits[5] = GL.glIsEnabled(eGL_SCISSOR_TEST) != 0;
|
|
|
|
if(HasExt[EXT_transform_feedback])
|
|
enableBits[6] = GL.glIsEnabled(eGL_RASTERIZER_DISCARD) != 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 && HasExt[ARB_clip_control])
|
|
{
|
|
GL.glGetIntegerv(eGL_CLIP_ORIGIN, (GLint *)&ClipOrigin);
|
|
GL.glGetIntegerv(eGL_CLIP_DEPTH_MODE, (GLint *)&ClipDepth);
|
|
}
|
|
else
|
|
{
|
|
ClipOrigin = eGL_LOWER_LEFT;
|
|
ClipDepth = eGL_NEGATIVE_ONE_TO_ONE;
|
|
}
|
|
|
|
if(modern && HasExt[ARB_draw_buffers_blend])
|
|
{
|
|
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_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);
|
|
}
|
|
|
|
if(modern && (HasExt[EXT_draw_buffers2] || HasExt[ARB_draw_buffers_blend]))
|
|
{
|
|
GL.glGetBooleani_v(eGL_COLOR_WRITEMASK, 0, ColorMask);
|
|
}
|
|
else
|
|
{
|
|
GL.glGetBooleanv(eGL_COLOR_WRITEMASK, ColorMask);
|
|
}
|
|
|
|
if(!VendorCheck[VendorCheck_AMD_polygon_mode_query] && !IsGLES)
|
|
{
|
|
GLenum dummy[2] = {eGL_FILL, eGL_FILL};
|
|
// docs suggest this is enumeration[2] even though polygon mode can't be set independently for
|
|
// front and back faces.
|
|
GL.glGetIntegerv(eGL_POLYGON_MODE, (GLint *)&dummy);
|
|
PolygonMode = dummy[0];
|
|
}
|
|
else
|
|
{
|
|
PolygonMode = eGL_FILL;
|
|
}
|
|
|
|
if(modern && HasExt[ARB_viewport_array])
|
|
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.glGetIntegerv(eGL_ARRAY_BUFFER_BINDING, (GLint *)&arraybuf);
|
|
|
|
// we get the current program but only try to restore it if it's non-0
|
|
prog = 0;
|
|
if(modern)
|
|
GL.glGetIntegerv(eGL_CURRENT_PROGRAM, (GLint *)&prog);
|
|
|
|
drawFBO = 0;
|
|
GL.glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, (GLint *)&drawFBO);
|
|
|
|
// 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;
|
|
if(modern && HasExt[ARB_separate_shader_objects])
|
|
GL.glGetIntegerv(eGL_PROGRAM_PIPELINE_BINDING, (GLint *)&pipe);
|
|
|
|
if(modern)
|
|
{
|
|
// the non-indexed bind is separate from the indexed binds
|
|
GL.glGetIntegerv(eGL_UNIFORM_BUFFER_BINDING, (GLint *)&ubo);
|
|
|
|
for(size_t i = 0; i < ARRAY_COUNT(idxubo); i++)
|
|
{
|
|
GL.glGetIntegeri_v(eGL_UNIFORM_BUFFER_BINDING, (GLuint)i, (GLint *)&idxubo[i].buf);
|
|
GL.glGetInteger64i_v(eGL_UNIFORM_BUFFER_START, (GLuint)i, (GLint64 *)&idxubo[i].offs);
|
|
GL.glGetInteger64i_v(eGL_UNIFORM_BUFFER_SIZE, (GLuint)i, (GLint64 *)&idxubo[i].size);
|
|
}
|
|
|
|
GL.glGetIntegerv(eGL_VERTEX_ARRAY_BINDING, (GLint *)&VAO);
|
|
}
|
|
|
|
ClearGLErrors();
|
|
}
|
|
|
|
void GLPushPopState::Pop(bool modern)
|
|
{
|
|
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(modern)
|
|
{
|
|
if(!IsGLES)
|
|
{
|
|
if(enableBits[3])
|
|
GL.glEnable(eGL_DEPTH_CLAMP);
|
|
else
|
|
GL.glDisable(eGL_DEPTH_CLAMP);
|
|
}
|
|
|
|
if(HasExt[ARB_draw_buffers_blend])
|
|
{
|
|
if(enableBits[4])
|
|
GL.glEnablei(eGL_BLEND, 0);
|
|
else
|
|
GL.glDisablei(eGL_BLEND, 0);
|
|
}
|
|
else
|
|
{
|
|
if(enableBits[4])
|
|
GL.glEnable(eGL_BLEND);
|
|
else
|
|
GL.glDisable(eGL_BLEND);
|
|
}
|
|
|
|
if(HasExt[ARB_viewport_array])
|
|
{
|
|
if(enableBits[5])
|
|
GL.glEnablei(eGL_SCISSOR_TEST, 0);
|
|
else
|
|
GL.glDisablei(eGL_SCISSOR_TEST, 0);
|
|
}
|
|
else
|
|
{
|
|
if(enableBits[5])
|
|
GL.glEnable(eGL_SCISSOR_TEST);
|
|
else
|
|
GL.glDisable(eGL_SCISSOR_TEST);
|
|
}
|
|
|
|
if(HasExt[EXT_transform_feedback])
|
|
{
|
|
if(enableBits[6])
|
|
GL.glEnable(eGL_RASTERIZER_DISCARD);
|
|
else
|
|
GL.glDisable(eGL_RASTERIZER_DISCARD);
|
|
}
|
|
}
|
|
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 && HasExt[ARB_clip_control])
|
|
GL.glClipControl(ClipOrigin, ClipDepth);
|
|
|
|
if(modern && HasExt[ARB_draw_buffers_blend])
|
|
{
|
|
GL.glBlendFuncSeparatei(0, SourceRGB, DestinationRGB, SourceAlpha, DestinationAlpha);
|
|
GL.glBlendEquationSeparatei(0, EquationRGB, EquationAlpha);
|
|
}
|
|
else
|
|
{
|
|
GL.glBlendFuncSeparate(SourceRGB, DestinationRGB, SourceAlpha, DestinationAlpha);
|
|
GL.glBlendEquationSeparate(EquationRGB, EquationAlpha);
|
|
}
|
|
|
|
if(modern && (HasExt[EXT_draw_buffers2] || HasExt[ARB_draw_buffers_blend]))
|
|
{
|
|
GL.glColorMaski(0, ColorMask[0], ColorMask[1], ColorMask[2], ColorMask[3]);
|
|
}
|
|
else
|
|
{
|
|
GL.glColorMask(ColorMask[0], ColorMask[1], ColorMask[2], ColorMask[3]);
|
|
}
|
|
|
|
if(!IsGLES)
|
|
GL.glPolygonMode(eGL_FRONT_AND_BACK, PolygonMode);
|
|
|
|
if(modern && HasExt[ARB_viewport_array])
|
|
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.glBindBuffer(eGL_ARRAY_BUFFER, arraybuf);
|
|
|
|
if(drawFBO != 0 && GL.glBindFramebuffer)
|
|
GL.glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, drawFBO);
|
|
|
|
if(modern)
|
|
{
|
|
for(size_t i = 0; i < ARRAY_COUNT(idxubo); i++)
|
|
{
|
|
if(idxubo[i].offs == 0 && idxubo[i].size == 0)
|
|
GL.glBindBufferBase(eGL_UNIFORM_BUFFER, (GLuint)i, idxubo[i].buf);
|
|
else
|
|
GL.glBindBufferRange(eGL_UNIFORM_BUFFER, (GLuint)i, idxubo[i].buf, (GLintptr)idxubo[i].offs,
|
|
(GLsizeiptr)idxubo[i].size);
|
|
}
|
|
|
|
GL.glBindBuffer(eGL_UNIFORM_BUFFER, ubo);
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
GLInitParams::GLInitParams()
|
|
{
|
|
colorBits = 32;
|
|
depthBits = 32;
|
|
stencilBits = 8;
|
|
isSRGB = 1;
|
|
multiSamples = 1;
|
|
width = 32;
|
|
height = 32;
|
|
isYFlipped = false;
|
|
}
|
|
|
|
bool GLInitParams::IsSupportedVersion(uint64_t ver)
|
|
{
|
|
if(ver == CurrentVersion)
|
|
return true;
|
|
|
|
// 0x1A -> 0x1B - supported MSAA and Multiview framebuffer attachments, which added
|
|
// number of samples, number of views, and base view index to the serialized data
|
|
if(ver == 0x1A)
|
|
return true;
|
|
|
|
// 0x1B -> 0x1C - fixed incorrect float/double serialisation in serialisation of
|
|
// ProgramUniformValue
|
|
if(ver == 0x1B)
|
|
return true;
|
|
|
|
// 0x1C -> 0x1D - added isYFlipped init parameter for backbuffers on ANGLE.
|
|
if(ver == 0x1C)
|
|
return true;
|
|
|
|
// 0x1D -> 0x1E - added new chunk for context parameters and per-context tracking of backbuffers
|
|
if(ver == 0x1D)
|
|
return true;
|
|
|
|
// 0x1E -> 0x1F - added initial states for samplers that are modified a lot
|
|
if(ver == 0x1E)
|
|
return true;
|
|
|
|
// 0x1F -> 0x20 - added renderer and version in GLInitParams
|
|
if(ver == 0x1F)
|
|
return true;
|
|
|
|
// 0x20 -> 0x21 - added multisampled texture initial data
|
|
if(ver == 0x20)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
template <typename SerialiserType>
|
|
void DoSerialise(SerialiserType &ser, GLInitParams &el)
|
|
{
|
|
SERIALISE_MEMBER(colorBits);
|
|
SERIALISE_MEMBER(depthBits);
|
|
SERIALISE_MEMBER(stencilBits);
|
|
SERIALISE_MEMBER(isSRGB);
|
|
SERIALISE_MEMBER(multiSamples);
|
|
SERIALISE_MEMBER(width);
|
|
SERIALISE_MEMBER(height);
|
|
if(ser.VersionAtLeast(0x1D))
|
|
SERIALISE_MEMBER(isYFlipped);
|
|
if(ser.VersionAtLeast(0x20))
|
|
{
|
|
SERIALISE_MEMBER(renderer);
|
|
SERIALISE_MEMBER(version);
|
|
}
|
|
}
|
|
|
|
INSTANTIATE_SERIALISE_TYPE(GLInitParams);
|
|
|
|
template <typename SerialiserType>
|
|
void DoSerialise(SerialiserType &ser, DrawElementsIndirectCommand &el)
|
|
{
|
|
SERIALISE_MEMBER(count);
|
|
SERIALISE_MEMBER(instanceCount);
|
|
SERIALISE_MEMBER(firstIndex);
|
|
SERIALISE_MEMBER(baseVertex);
|
|
SERIALISE_MEMBER(baseInstance);
|
|
}
|
|
|
|
INSTANTIATE_SERIALISE_TYPE(DrawElementsIndirectCommand);
|
|
|
|
template <typename SerialiserType>
|
|
void DoSerialise(SerialiserType &ser, DrawArraysIndirectCommand &el)
|
|
{
|
|
SERIALISE_MEMBER(count);
|
|
SERIALISE_MEMBER(instanceCount);
|
|
SERIALISE_MEMBER(first);
|
|
SERIALISE_MEMBER(baseInstance);
|
|
}
|
|
|
|
INSTANTIATE_SERIALISE_TYPE(DrawArraysIndirectCommand);
|
|
|
|
size_t BufferIdx(GLenum buf)
|
|
{
|
|
switch(buf)
|
|
{
|
|
case eGL_ARRAY_BUFFER: return 0;
|
|
case eGL_ATOMIC_COUNTER_BUFFER: return 1;
|
|
case eGL_COPY_READ_BUFFER: return 2;
|
|
case eGL_COPY_WRITE_BUFFER: return 3;
|
|
case eGL_DRAW_INDIRECT_BUFFER: return 4;
|
|
case eGL_DISPATCH_INDIRECT_BUFFER: return 5;
|
|
case eGL_ELEMENT_ARRAY_BUFFER: return 6;
|
|
case eGL_PIXEL_PACK_BUFFER: return 7;
|
|
case eGL_PIXEL_UNPACK_BUFFER: return 8;
|
|
case eGL_QUERY_BUFFER: return 9;
|
|
case eGL_SHADER_STORAGE_BUFFER: return 10;
|
|
case eGL_TEXTURE_BUFFER: return 11;
|
|
case eGL_TRANSFORM_FEEDBACK_BUFFER: return 12;
|
|
case eGL_UNIFORM_BUFFER: return 13;
|
|
case eGL_PARAMETER_BUFFER_ARB: return 14;
|
|
default: RDCERR("Unexpected enum as buffer target: %s", ToStr(buf).c_str());
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
GLenum BufferEnum(size_t idx)
|
|
{
|
|
GLenum enums[] = {
|
|
eGL_ARRAY_BUFFER,
|
|
eGL_ATOMIC_COUNTER_BUFFER,
|
|
eGL_COPY_READ_BUFFER,
|
|
eGL_COPY_WRITE_BUFFER,
|
|
eGL_DRAW_INDIRECT_BUFFER,
|
|
eGL_DISPATCH_INDIRECT_BUFFER,
|
|
eGL_ELEMENT_ARRAY_BUFFER,
|
|
eGL_PIXEL_PACK_BUFFER,
|
|
eGL_PIXEL_UNPACK_BUFFER,
|
|
eGL_QUERY_BUFFER,
|
|
eGL_SHADER_STORAGE_BUFFER,
|
|
eGL_TEXTURE_BUFFER,
|
|
eGL_TRANSFORM_FEEDBACK_BUFFER,
|
|
eGL_UNIFORM_BUFFER,
|
|
eGL_PARAMETER_BUFFER_ARB,
|
|
};
|
|
|
|
if(idx < ARRAY_COUNT(enums))
|
|
return enums[idx];
|
|
|
|
return eGL_NONE;
|
|
}
|
|
|
|
size_t TextureIdx(GLenum buf)
|
|
{
|
|
switch(buf)
|
|
{
|
|
case eGL_TEXTURE_1D: return 0;
|
|
case eGL_TEXTURE_1D_ARRAY: return 1;
|
|
case eGL_TEXTURE_2D: return 2;
|
|
case eGL_TEXTURE_2D_ARRAY: return 3;
|
|
case eGL_TEXTURE_2D_MULTISAMPLE: return 4;
|
|
case eGL_TEXTURE_2D_MULTISAMPLE_ARRAY: return 5;
|
|
case eGL_TEXTURE_RECTANGLE: return 6;
|
|
case eGL_TEXTURE_3D: return 7;
|
|
case eGL_TEXTURE_CUBE_MAP:
|
|
case eGL_TEXTURE_CUBE_MAP_POSITIVE_X:
|
|
case eGL_TEXTURE_CUBE_MAP_NEGATIVE_X:
|
|
case eGL_TEXTURE_CUBE_MAP_POSITIVE_Y:
|
|
case eGL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
|
|
case eGL_TEXTURE_CUBE_MAP_POSITIVE_Z:
|
|
case eGL_TEXTURE_CUBE_MAP_NEGATIVE_Z: return 8;
|
|
case eGL_TEXTURE_CUBE_MAP_ARRAY: return 9;
|
|
case eGL_TEXTURE_BUFFER: return 10;
|
|
default: RDCERR("Unexpected enum as texture target: %s", ToStr(buf).c_str());
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
GLenum TextureEnum(size_t idx)
|
|
{
|
|
GLenum enums[] = {
|
|
eGL_TEXTURE_1D,
|
|
eGL_TEXTURE_1D_ARRAY,
|
|
eGL_TEXTURE_2D,
|
|
eGL_TEXTURE_2D_ARRAY,
|
|
eGL_TEXTURE_2D_MULTISAMPLE,
|
|
eGL_TEXTURE_2D_MULTISAMPLE_ARRAY,
|
|
eGL_TEXTURE_RECTANGLE,
|
|
eGL_TEXTURE_3D,
|
|
eGL_TEXTURE_CUBE_MAP,
|
|
eGL_TEXTURE_CUBE_MAP_ARRAY,
|
|
eGL_TEXTURE_BUFFER,
|
|
};
|
|
|
|
if(idx < ARRAY_COUNT(enums))
|
|
return enums[idx];
|
|
|
|
return eGL_NONE;
|
|
}
|
|
|
|
size_t QueryIdx(GLenum query)
|
|
{
|
|
size_t idx = 0;
|
|
|
|
switch(query)
|
|
{
|
|
case eGL_SAMPLES_PASSED: idx = 0; break;
|
|
case eGL_ANY_SAMPLES_PASSED: idx = 1; break;
|
|
case eGL_ANY_SAMPLES_PASSED_CONSERVATIVE: idx = 2; break;
|
|
case eGL_PRIMITIVES_GENERATED: idx = 3; break;
|
|
case eGL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: idx = 4; break;
|
|
case eGL_TIME_ELAPSED: idx = 5; break;
|
|
case eGL_VERTICES_SUBMITTED_ARB: idx = 6; break;
|
|
case eGL_PRIMITIVES_SUBMITTED_ARB: idx = 7; break;
|
|
case eGL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB: idx = 8; break;
|
|
case eGL_CLIPPING_INPUT_PRIMITIVES_ARB: idx = 9; break;
|
|
case eGL_CLIPPING_OUTPUT_PRIMITIVES_ARB: idx = 10; break;
|
|
case eGL_VERTEX_SHADER_INVOCATIONS_ARB: idx = 11; break;
|
|
case eGL_TESS_CONTROL_SHADER_PATCHES_ARB: idx = 12; break;
|
|
case eGL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB: idx = 13; break;
|
|
case eGL_GEOMETRY_SHADER_INVOCATIONS: idx = 14; break;
|
|
case eGL_FRAGMENT_SHADER_INVOCATIONS_ARB: idx = 15; break;
|
|
case eGL_COMPUTE_SHADER_INVOCATIONS_ARB: idx = 16; break;
|
|
|
|
default: RDCERR("Unexpected enum as query target: %s", ToStr(query).c_str());
|
|
}
|
|
|
|
if(idx >= WrappedOpenGL::MAX_QUERIES)
|
|
RDCERR("Query index for enum %s out of range %d", ToStr(query).c_str(), idx);
|
|
|
|
return idx;
|
|
}
|
|
|
|
GLenum QueryEnum(size_t idx)
|
|
{
|
|
GLenum enums[] = {eGL_SAMPLES_PASSED,
|
|
eGL_ANY_SAMPLES_PASSED,
|
|
eGL_ANY_SAMPLES_PASSED_CONSERVATIVE,
|
|
eGL_PRIMITIVES_GENERATED,
|
|
eGL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN,
|
|
eGL_TIME_ELAPSED,
|
|
eGL_VERTICES_SUBMITTED_ARB,
|
|
eGL_PRIMITIVES_SUBMITTED_ARB,
|
|
eGL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB,
|
|
eGL_CLIPPING_INPUT_PRIMITIVES_ARB,
|
|
eGL_CLIPPING_OUTPUT_PRIMITIVES_ARB,
|
|
eGL_VERTEX_SHADER_INVOCATIONS_ARB,
|
|
eGL_TESS_CONTROL_SHADER_PATCHES_ARB,
|
|
eGL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB,
|
|
eGL_GEOMETRY_SHADER_INVOCATIONS,
|
|
eGL_FRAGMENT_SHADER_INVOCATIONS_ARB,
|
|
eGL_COMPUTE_SHADER_INVOCATIONS_ARB};
|
|
|
|
if(idx < ARRAY_COUNT(enums))
|
|
return enums[idx];
|
|
|
|
return eGL_NONE;
|
|
}
|
|
|
|
size_t GLTypeSize(GLenum type)
|
|
{
|
|
switch(type)
|
|
{
|
|
case eGL_UNSIGNED_BYTE:
|
|
case eGL_BYTE: return 1;
|
|
case eGL_UNSIGNED_SHORT:
|
|
case eGL_UNSIGNED_SHORT_5_6_5:
|
|
case eGL_SHORT:
|
|
case eGL_HALF_FLOAT_OES:
|
|
case eGL_HALF_FLOAT: return 2;
|
|
case eGL_UNSIGNED_INT:
|
|
case eGL_INT:
|
|
case eGL_FLOAT:
|
|
case eGL_UNSIGNED_INT_8_8_8_8_REV: return 4;
|
|
case eGL_DOUBLE: return 8;
|
|
default: RDCWARN("Unhandled element type %s", ToStr(type).c_str());
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
size_t ShaderIdx(GLenum buf)
|
|
{
|
|
switch(buf)
|
|
{
|
|
case eGL_VERTEX_SHADER: return 0;
|
|
case eGL_TESS_CONTROL_SHADER: return 1;
|
|
case eGL_TESS_EVALUATION_SHADER: return 2;
|
|
case eGL_GEOMETRY_SHADER: return 3;
|
|
case eGL_FRAGMENT_SHADER: return 4;
|
|
case eGL_COMPUTE_SHADER: return 5;
|
|
default: RDCERR("Unexpected enum as shader enum: %s", ToStr(buf).c_str());
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
GLenum ShaderBit(size_t idx)
|
|
{
|
|
GLenum enums[] = {
|
|
eGL_VERTEX_SHADER_BIT, eGL_TESS_CONTROL_SHADER_BIT, eGL_TESS_EVALUATION_SHADER_BIT,
|
|
eGL_GEOMETRY_SHADER_BIT, eGL_FRAGMENT_SHADER_BIT, eGL_COMPUTE_SHADER_BIT,
|
|
};
|
|
|
|
if(idx < ARRAY_COUNT(enums))
|
|
return enums[idx];
|
|
|
|
return eGL_NONE;
|
|
}
|
|
|
|
GLenum ShaderEnum(size_t idx)
|
|
{
|
|
GLenum enums[] = {
|
|
eGL_VERTEX_SHADER, eGL_TESS_CONTROL_SHADER, eGL_TESS_EVALUATION_SHADER,
|
|
eGL_GEOMETRY_SHADER, eGL_FRAGMENT_SHADER, eGL_COMPUTE_SHADER,
|
|
};
|
|
|
|
if(idx < ARRAY_COUNT(enums))
|
|
return enums[idx];
|
|
|
|
return eGL_NONE;
|
|
}
|
|
|
|
void ClearGLErrors()
|
|
{
|
|
int i = 0;
|
|
GLenum err = GL.glGetError();
|
|
while(err)
|
|
{
|
|
err = GL.glGetError();
|
|
i++;
|
|
if(i > 100)
|
|
{
|
|
RDCERR("Couldn't clear GL errors - something very wrong!");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
GLint GetNumVertexBuffers()
|
|
{
|
|
GLint numBindings = 16;
|
|
|
|
// when the extension isn't present we pretend attribs == vertex buffers
|
|
if(HasExt[ARB_vertex_attrib_binding])
|
|
GL.glGetIntegerv(eGL_MAX_VERTEX_ATTRIB_BINDINGS, &numBindings);
|
|
else
|
|
GL.glGetIntegerv(eGL_MAX_VERTEX_ATTRIBS, &numBindings);
|
|
|
|
return numBindings;
|
|
}
|
|
|
|
GLuint GetBoundVertexBuffer(GLuint i)
|
|
{
|
|
GLuint buffer = 0;
|
|
|
|
if(VendorCheck[VendorCheck_AMD_vertex_buffer_query] || !HasExt[ARB_vertex_attrib_binding])
|
|
GL.glGetVertexAttribiv(i, eGL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, (GLint *)&buffer);
|
|
else
|
|
GL.glGetIntegeri_v(eGL_VERTEX_BINDING_BUFFER, i, (GLint *)&buffer);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
void SafeBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0,
|
|
GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)
|
|
{
|
|
bool scissorEnabled = false;
|
|
GLboolean ColorMask[4] = {GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE};
|
|
GLboolean DepthMask = GL_TRUE;
|
|
GLint StencilMask = 0xff, StencilBackMask = 0xff;
|
|
|
|
// fetch current state
|
|
{
|
|
if(HasExt[ARB_viewport_array])
|
|
scissorEnabled = GL.glIsEnabledi(eGL_SCISSOR_TEST, 0) != 0;
|
|
else
|
|
scissorEnabled = GL.glIsEnabled(eGL_SCISSOR_TEST) != 0;
|
|
|
|
if(HasExt[EXT_draw_buffers2] || HasExt[ARB_draw_buffers_blend])
|
|
GL.glGetBooleani_v(eGL_COLOR_WRITEMASK, 0, ColorMask);
|
|
else
|
|
GL.glGetBooleanv(eGL_COLOR_WRITEMASK, ColorMask);
|
|
|
|
GL.glGetBooleanv(eGL_DEPTH_WRITEMASK, &DepthMask);
|
|
|
|
GL.glGetIntegerv(eGL_STENCIL_WRITEMASK, &StencilMask);
|
|
GL.glGetIntegerv(eGL_STENCIL_BACK_WRITEMASK, &StencilBackMask);
|
|
}
|
|
|
|
// apply safe state
|
|
{
|
|
if(HasExt[ARB_viewport_array])
|
|
GL.glDisablei(eGL_SCISSOR_TEST, 0);
|
|
else
|
|
GL.glDisable(eGL_SCISSOR_TEST);
|
|
|
|
if(HasExt[EXT_draw_buffers2] || HasExt[ARB_draw_buffers_blend])
|
|
GL.glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
else
|
|
GL.glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
|
|
GL.glDepthMask(GL_TRUE);
|
|
|
|
GL.glStencilMaskSeparate(eGL_FRONT, 0xff);
|
|
GL.glStencilMaskSeparate(eGL_BACK, 0xff);
|
|
}
|
|
|
|
GL.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
|
|
|
|
// restore original state
|
|
{
|
|
if(HasExt[ARB_viewport_array])
|
|
{
|
|
if(scissorEnabled)
|
|
GL.glEnablei(eGL_SCISSOR_TEST, 0);
|
|
else
|
|
GL.glDisablei(eGL_SCISSOR_TEST, 0);
|
|
}
|
|
else
|
|
{
|
|
if(scissorEnabled)
|
|
GL.glEnable(eGL_SCISSOR_TEST);
|
|
else
|
|
GL.glDisable(eGL_SCISSOR_TEST);
|
|
}
|
|
|
|
if(HasExt[EXT_draw_buffers2] || HasExt[ARB_draw_buffers_blend])
|
|
GL.glColorMaski(0, ColorMask[0], ColorMask[1], ColorMask[2], ColorMask[3]);
|
|
else
|
|
GL.glColorMask(ColorMask[0], ColorMask[1], ColorMask[2], ColorMask[3]);
|
|
|
|
GL.glDepthMask(DepthMask);
|
|
|
|
GL.glStencilMaskSeparate(eGL_FRONT, StencilMask);
|
|
GL.glStencilMaskSeparate(eGL_BACK, StencilBackMask);
|
|
}
|
|
}
|
|
|
|
BufferCategory MakeBufferCategory(GLenum bufferTarget)
|
|
{
|
|
switch(bufferTarget)
|
|
{
|
|
case eGL_ARRAY_BUFFER: return BufferCategory::Vertex;
|
|
case eGL_ELEMENT_ARRAY_BUFFER: return BufferCategory::Index;
|
|
case eGL_UNIFORM_BUFFER: return BufferCategory::Constants;
|
|
case eGL_SHADER_STORAGE_BUFFER: return BufferCategory::ReadWrite;
|
|
case eGL_DRAW_INDIRECT_BUFFER:
|
|
case eGL_DISPATCH_INDIRECT_BUFFER:
|
|
case eGL_PARAMETER_BUFFER_ARB: return BufferCategory::Indirect;
|
|
default: break;
|
|
}
|
|
return BufferCategory::NoFlags;
|
|
}
|
|
|
|
AddressMode MakeAddressMode(GLenum addr)
|
|
{
|
|
switch(addr)
|
|
{
|
|
case eGL_REPEAT: return AddressMode::Wrap;
|
|
case eGL_MIRRORED_REPEAT: return AddressMode::Mirror;
|
|
case eGL_CLAMP_TO_EDGE: return AddressMode::ClampEdge;
|
|
case eGL_CLAMP_TO_BORDER: return AddressMode::ClampBorder;
|
|
case eGL_MIRROR_CLAMP_TO_EDGE: return AddressMode::MirrorOnce;
|
|
default: break;
|
|
}
|
|
|
|
return AddressMode::Wrap;
|
|
}
|
|
|
|
TextureFilter MakeFilter(GLenum minf, GLenum magf, bool shadowSampler, float maxAniso)
|
|
{
|
|
TextureFilter ret;
|
|
|
|
if(maxAniso > 1.0f)
|
|
{
|
|
ret.minify = ret.magnify = ret.mip = FilterMode::Anisotropic;
|
|
}
|
|
else
|
|
{
|
|
if(minf == eGL_NEAREST || minf == eGL_LINEAR)
|
|
{
|
|
ret.minify = (minf == eGL_LINEAR) ? FilterMode::Linear : FilterMode::Point;
|
|
ret.mip = FilterMode::NoFilter;
|
|
}
|
|
else if(minf == eGL_NEAREST_MIPMAP_LINEAR || minf == eGL_LINEAR_MIPMAP_LINEAR)
|
|
{
|
|
ret.minify = (minf == eGL_LINEAR_MIPMAP_LINEAR) ? FilterMode::Linear : FilterMode::Point;
|
|
ret.mip = FilterMode::Linear;
|
|
}
|
|
else if(minf == eGL_NEAREST_MIPMAP_NEAREST || minf == eGL_LINEAR_MIPMAP_NEAREST)
|
|
{
|
|
ret.minify = (minf == eGL_LINEAR_MIPMAP_NEAREST) ? FilterMode::Linear : FilterMode::Point;
|
|
ret.mip = FilterMode::Point;
|
|
}
|
|
|
|
ret.magnify = (magf == eGL_LINEAR) ? FilterMode::Linear : FilterMode::Point;
|
|
}
|
|
ret.filter = shadowSampler ? FilterFunction::Comparison : FilterFunction::Normal;
|
|
|
|
return ret;
|
|
}
|
|
|
|
ShaderStage MakeShaderStage(GLenum type)
|
|
{
|
|
switch(type)
|
|
{
|
|
case eGL_VERTEX_SHADER: return ShaderStage::Vertex;
|
|
case eGL_TESS_CONTROL_SHADER: return ShaderStage::Tess_Control;
|
|
case eGL_TESS_EVALUATION_SHADER: return ShaderStage::Tess_Eval;
|
|
case eGL_GEOMETRY_SHADER: return ShaderStage::Geometry;
|
|
case eGL_FRAGMENT_SHADER: return ShaderStage::Fragment;
|
|
case eGL_COMPUTE_SHADER: return ShaderStage::Compute;
|
|
default: RDCERR("Unexpected shader stage %s", ToStr(type).c_str());
|
|
}
|
|
|
|
return ShaderStage::Count;
|
|
}
|
|
|
|
CompareFunction MakeCompareFunc(GLenum func)
|
|
{
|
|
switch(func)
|
|
{
|
|
case eGL_NEVER: return CompareFunction::Never;
|
|
case eGL_LESS: return CompareFunction::Less;
|
|
case eGL_EQUAL: return CompareFunction::Equal;
|
|
case eGL_LEQUAL: return CompareFunction::LessEqual;
|
|
case eGL_GREATER: return CompareFunction::Greater;
|
|
case eGL_NOTEQUAL: return CompareFunction::NotEqual;
|
|
case eGL_GEQUAL: return CompareFunction::GreaterEqual;
|
|
case eGL_ALWAYS: return CompareFunction::AlwaysTrue;
|
|
default: break;
|
|
}
|
|
|
|
return CompareFunction::AlwaysTrue;
|
|
}
|
|
|
|
StencilOperation MakeStencilOp(GLenum op)
|
|
{
|
|
switch(op)
|
|
{
|
|
case eGL_KEEP: return StencilOperation::Keep;
|
|
case eGL_ZERO: return StencilOperation::Zero;
|
|
case eGL_REPLACE: return StencilOperation::Replace;
|
|
case eGL_INCR: return StencilOperation::IncSat;
|
|
case eGL_DECR: return StencilOperation::DecSat;
|
|
case eGL_INVERT: return StencilOperation::Invert;
|
|
case eGL_INCR_WRAP: return StencilOperation::IncWrap;
|
|
case eGL_DECR_WRAP: return StencilOperation::DecWrap;
|
|
default: break;
|
|
}
|
|
|
|
return StencilOperation::Keep;
|
|
}
|
|
|
|
LogicOperation MakeLogicOp(GLenum op)
|
|
{
|
|
switch(op)
|
|
{
|
|
case eGL_CLEAR: return LogicOperation::Clear;
|
|
case eGL_AND: return LogicOperation::And;
|
|
case eGL_AND_REVERSE: return LogicOperation::AndReverse;
|
|
case eGL_COPY: return LogicOperation::Copy;
|
|
case eGL_AND_INVERTED: return LogicOperation::AndInverted;
|
|
case eGL_NOOP: return LogicOperation::NoOp;
|
|
case eGL_XOR: return LogicOperation::Xor;
|
|
case eGL_OR: return LogicOperation::Or;
|
|
case eGL_NOR: return LogicOperation::Nor;
|
|
case eGL_EQUIV: return LogicOperation::Equivalent;
|
|
case eGL_INVERT: return LogicOperation::Invert;
|
|
case eGL_OR_REVERSE: return LogicOperation::OrReverse;
|
|
case eGL_COPY_INVERTED: return LogicOperation::CopyInverted;
|
|
case eGL_OR_INVERTED: return LogicOperation::OrInverted;
|
|
case eGL_NAND: return LogicOperation::Nand;
|
|
case eGL_SET: return LogicOperation::Set;
|
|
default: break;
|
|
}
|
|
|
|
return LogicOperation::NoOp;
|
|
}
|
|
|
|
BlendMultiplier MakeBlendMultiplier(GLenum blend)
|
|
{
|
|
switch(blend)
|
|
{
|
|
case eGL_ZERO: return BlendMultiplier::Zero;
|
|
case eGL_ONE: return BlendMultiplier::One;
|
|
case eGL_SRC_COLOR: return BlendMultiplier::SrcCol;
|
|
case eGL_ONE_MINUS_SRC_COLOR: return BlendMultiplier::InvSrcCol;
|
|
case eGL_DST_COLOR: return BlendMultiplier::DstCol;
|
|
case eGL_ONE_MINUS_DST_COLOR: return BlendMultiplier::InvDstCol;
|
|
case eGL_SRC_ALPHA: return BlendMultiplier::SrcAlpha;
|
|
case eGL_ONE_MINUS_SRC_ALPHA: return BlendMultiplier::InvSrcAlpha;
|
|
case eGL_DST_ALPHA: return BlendMultiplier::DstAlpha;
|
|
case eGL_ONE_MINUS_DST_ALPHA: return BlendMultiplier::InvDstAlpha;
|
|
case eGL_CONSTANT_COLOR: return BlendMultiplier::FactorRGB;
|
|
case eGL_ONE_MINUS_CONSTANT_COLOR: return BlendMultiplier::InvFactorRGB;
|
|
case eGL_CONSTANT_ALPHA: return BlendMultiplier::FactorAlpha;
|
|
case eGL_ONE_MINUS_CONSTANT_ALPHA: return BlendMultiplier::InvFactorAlpha;
|
|
case eGL_SRC_ALPHA_SATURATE: return BlendMultiplier::SrcAlphaSat;
|
|
case eGL_SRC1_COLOR: return BlendMultiplier::Src1Col;
|
|
case eGL_ONE_MINUS_SRC1_COLOR: return BlendMultiplier::InvSrc1Col;
|
|
case eGL_SRC1_ALPHA: return BlendMultiplier::Src1Alpha;
|
|
case eGL_ONE_MINUS_SRC1_ALPHA: return BlendMultiplier::InvSrc1Alpha;
|
|
default: break;
|
|
}
|
|
|
|
return BlendMultiplier::One;
|
|
}
|
|
|
|
BlendOperation MakeBlendOp(GLenum op)
|
|
{
|
|
switch(op)
|
|
{
|
|
case eGL_FUNC_ADD: return BlendOperation::Add;
|
|
case eGL_FUNC_SUBTRACT: return BlendOperation::Subtract;
|
|
case eGL_FUNC_REVERSE_SUBTRACT: return BlendOperation::ReversedSubtract;
|
|
case eGL_MIN: return BlendOperation::Minimum;
|
|
case eGL_MAX: return BlendOperation::Maximum;
|
|
default: break;
|
|
}
|
|
|
|
return BlendOperation::Add;
|
|
}
|
|
|
|
ResourceFormat MakeResourceFormat(GLenum target, GLenum fmt)
|
|
{
|
|
ResourceFormat ret;
|
|
|
|
ret.type = ResourceFormatType::Regular;
|
|
|
|
if(fmt == eGL_NONE)
|
|
{
|
|
ret.type = ResourceFormatType::Undefined;
|
|
return ret;
|
|
}
|
|
|
|
// special handling for formats that don't query neatly
|
|
if(fmt == eGL_LUMINANCE8_EXT || fmt == eGL_INTENSITY8_EXT || fmt == eGL_LUMINANCE)
|
|
{
|
|
ret.compByteWidth = 1;
|
|
ret.compCount = 1;
|
|
ret.compType = CompType::UNorm;
|
|
return ret;
|
|
}
|
|
else if(fmt == eGL_ALPHA || fmt == eGL_ALPHA8_EXT)
|
|
{
|
|
ret.compByteWidth = 1;
|
|
ret.compCount = 1;
|
|
ret.compType = CompType::UNorm;
|
|
ret.type = ResourceFormatType::A8;
|
|
return ret;
|
|
}
|
|
else if(fmt == eGL_LUMINANCE8_ALPHA8_EXT || fmt == eGL_LUMINANCE_ALPHA)
|
|
{
|
|
ret.compByteWidth = 1;
|
|
ret.compCount = 2;
|
|
ret.compType = CompType::UNorm;
|
|
return ret;
|
|
}
|
|
else if(fmt == eGL_RGBA2)
|
|
{
|
|
// pretend it's RGBA8 and hope for the best
|
|
ret.compByteWidth = 1;
|
|
ret.compCount = 4;
|
|
ret.compType = CompType::UNorm;
|
|
return ret;
|
|
}
|
|
|
|
if(IsCompressedFormat(fmt))
|
|
{
|
|
switch(fmt)
|
|
{
|
|
// BC1
|
|
case eGL_COMPRESSED_RGB_S3TC_DXT1_EXT:
|
|
case eGL_COMPRESSED_SRGB_S3TC_DXT1_EXT: ret.compCount = 3; break;
|
|
case eGL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
|
|
case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
|
|
ret.compCount = 4;
|
|
break;
|
|
|
|
// BC2
|
|
case eGL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
|
|
case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
|
|
ret.compCount = 4;
|
|
break;
|
|
|
|
// BC3
|
|
case eGL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
|
case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
|
|
ret.compCount = 4;
|
|
break;
|
|
// BC4
|
|
case eGL_COMPRESSED_RED_RGTC1:
|
|
case eGL_COMPRESSED_SIGNED_RED_RGTC1:
|
|
ret.compCount = 1;
|
|
break;
|
|
// BC5
|
|
case eGL_COMPRESSED_RG_RGTC2:
|
|
case eGL_COMPRESSED_SIGNED_RG_RGTC2:
|
|
ret.compCount = 2;
|
|
break;
|
|
// BC6
|
|
case eGL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB:
|
|
case eGL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB:
|
|
ret.compCount = 3;
|
|
break;
|
|
// BC7
|
|
case eGL_COMPRESSED_RGBA_BPTC_UNORM_ARB:
|
|
case eGL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB: ret.compCount = 4; break;
|
|
|
|
case eGL_COMPRESSED_RGBA8_ETC2_EAC:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: ret.compCount = 4; break;
|
|
case eGL_COMPRESSED_R11_EAC:
|
|
case eGL_COMPRESSED_SIGNED_R11_EAC: ret.compCount = 1; break;
|
|
case eGL_COMPRESSED_RG11_EAC:
|
|
case eGL_COMPRESSED_SIGNED_RG11_EAC: ret.compCount = 2; break;
|
|
|
|
case eGL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT:
|
|
case eGL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT: ret.compCount = 3; break;
|
|
case eGL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT:
|
|
case eGL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT: ret.compCount = 4; break;
|
|
|
|
case eGL_ETC1_RGB8_OES:
|
|
case eGL_COMPRESSED_RGB8_ETC2:
|
|
case eGL_COMPRESSED_SRGB8_ETC2: ret.compCount = 3; break;
|
|
case eGL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
|
|
case eGL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: ret.compCount = 4; break;
|
|
|
|
case eGL_COMPRESSED_RGBA_ASTC_4x4_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_5x4_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_5x5_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_6x5_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_6x6_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_8x5_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_8x6_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_8x8_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_10x5_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_10x6_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_10x8_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_10x10_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_12x10_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_12x12_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: ret.compCount = 4; break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
ret.compType = CompType::UNorm;
|
|
|
|
switch(fmt)
|
|
{
|
|
case eGL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
|
|
case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
|
|
case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
|
|
case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
|
|
case eGL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB:
|
|
case eGL_COMPRESSED_SRGB8_ETC2:
|
|
case eGL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: ret.compType = CompType::UNormSRGB; break;
|
|
default: break;
|
|
}
|
|
|
|
switch(fmt)
|
|
{
|
|
case eGL_COMPRESSED_SIGNED_RED_RGTC1:
|
|
case eGL_COMPRESSED_SIGNED_RG_RGTC2:
|
|
case eGL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB:
|
|
case eGL_COMPRESSED_SIGNED_R11_EAC:
|
|
case eGL_COMPRESSED_SIGNED_RG11_EAC: ret.compType = CompType::SNorm; break;
|
|
default: break;
|
|
}
|
|
|
|
ret.type = ResourceFormatType::Undefined;
|
|
|
|
switch(fmt)
|
|
{
|
|
// BC1
|
|
case eGL_COMPRESSED_RGB_S3TC_DXT1_EXT:
|
|
case eGL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
|
|
case eGL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
|
|
case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
|
|
ret.type = ResourceFormatType::BC1;
|
|
break;
|
|
// BC2
|
|
case eGL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
|
|
case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
|
|
ret.type = ResourceFormatType::BC2;
|
|
break;
|
|
// BC3
|
|
case eGL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
|
case eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
|
|
ret.type = ResourceFormatType::BC3;
|
|
break;
|
|
// BC4
|
|
case eGL_COMPRESSED_RED_RGTC1:
|
|
case eGL_COMPRESSED_SIGNED_RED_RGTC1:
|
|
ret.type = ResourceFormatType::BC4;
|
|
break;
|
|
// BC5
|
|
case eGL_COMPRESSED_RG_RGTC2:
|
|
case eGL_COMPRESSED_SIGNED_RG_RGTC2:
|
|
ret.type = ResourceFormatType::BC5;
|
|
break;
|
|
// BC6
|
|
case eGL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB:
|
|
case eGL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB:
|
|
ret.type = ResourceFormatType::BC6;
|
|
break;
|
|
// BC7
|
|
case eGL_COMPRESSED_RGBA_BPTC_UNORM_ARB:
|
|
case eGL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB:
|
|
ret.type = ResourceFormatType::BC7;
|
|
break;
|
|
// ETC1
|
|
case eGL_ETC1_RGB8_OES: // handle it as ETC2
|
|
// ETC2
|
|
case eGL_COMPRESSED_RGB8_ETC2:
|
|
case eGL_COMPRESSED_SRGB8_ETC2:
|
|
case eGL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
|
|
case eGL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
|
|
ret.type = ResourceFormatType::ETC2;
|
|
break;
|
|
// EAC
|
|
case eGL_COMPRESSED_RGBA8_ETC2_EAC:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
|
|
case eGL_COMPRESSED_R11_EAC:
|
|
case eGL_COMPRESSED_SIGNED_R11_EAC:
|
|
case eGL_COMPRESSED_RG11_EAC:
|
|
case eGL_COMPRESSED_SIGNED_RG11_EAC:
|
|
ret.type = ResourceFormatType::EAC;
|
|
break;
|
|
// ASTC
|
|
case eGL_COMPRESSED_RGBA_ASTC_4x4_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_5x4_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_5x5_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_6x5_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_6x6_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_8x5_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_8x6_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_8x8_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_10x5_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_10x6_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_10x8_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_10x10_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_12x10_KHR:
|
|
case eGL_COMPRESSED_RGBA_ASTC_12x12_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:
|
|
case eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:
|
|
ret.type = ResourceFormatType::ASTC;
|
|
break;
|
|
// PVRTC
|
|
case eGL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT:
|
|
case eGL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT:
|
|
case eGL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT:
|
|
case eGL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT: ret.type = ResourceFormatType::PVRTC; break;
|
|
default: RDCERR("Unexpected compressed format %#x", fmt); break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// handle certain non compressed but special formats
|
|
switch(fmt)
|
|
{
|
|
case eGL_R11F_G11F_B10F:
|
|
ret.type = ResourceFormatType::R11G11B10;
|
|
ret.compType = CompType::Float;
|
|
ret.compCount = 3;
|
|
break;
|
|
case eGL_RGB565:
|
|
ret.type = ResourceFormatType::R5G6B5;
|
|
ret.compType = CompType::UNorm;
|
|
ret.compCount = 3;
|
|
break;
|
|
case eGL_RGB5_A1:
|
|
ret.type = ResourceFormatType::R5G5B5A1;
|
|
ret.compType = CompType::UNorm;
|
|
ret.compCount = 4;
|
|
break;
|
|
case eGL_RGB9_E5:
|
|
ret.type = ResourceFormatType::R9G9B9E5;
|
|
ret.compType = CompType::Float;
|
|
ret.compCount = 3;
|
|
break;
|
|
case eGL_RGBA4:
|
|
ret.type = ResourceFormatType::R4G4B4A4;
|
|
ret.compType = CompType::UNorm;
|
|
ret.compCount = 4;
|
|
break;
|
|
case eGL_RGB10_A2:
|
|
case eGL_RGB10_A2UI:
|
|
ret.type = ResourceFormatType::R10G10B10A2;
|
|
ret.compType = fmt == eGL_RGB10_A2 ? CompType::UNorm : CompType::UInt;
|
|
ret.compCount = 4;
|
|
break;
|
|
default: break;
|
|
}
|
|
|
|
if(ret.Special())
|
|
return ret;
|
|
|
|
ret.compByteWidth = 1;
|
|
ret.compCount = 4;
|
|
ret.compType = CompType::Float;
|
|
|
|
GLint data[8];
|
|
GLenum *edata = (GLenum *)data;
|
|
|
|
GLint iscol = 0, isdepth = 0, isstencil = 0;
|
|
GL.glGetInternalformativ(target, fmt, eGL_COLOR_COMPONENTS, sizeof(GLint), &iscol);
|
|
GL.glGetInternalformativ(target, fmt, eGL_DEPTH_COMPONENTS, sizeof(GLint), &isdepth);
|
|
GL.glGetInternalformativ(target, fmt, eGL_STENCIL_COMPONENTS, sizeof(GLint), &isstencil);
|
|
|
|
if(iscol == GL_TRUE)
|
|
{
|
|
if(fmt == eGL_BGRA8_EXT || fmt == eGL_BGRA)
|
|
ret.SetBGRAOrder(true);
|
|
|
|
// colour format
|
|
|
|
GL.glGetInternalformativ(target, fmt, eGL_INTERNALFORMAT_RED_SIZE, sizeof(GLint), &data[0]);
|
|
GL.glGetInternalformativ(target, fmt, eGL_INTERNALFORMAT_GREEN_SIZE, sizeof(GLint), &data[1]);
|
|
GL.glGetInternalformativ(target, fmt, eGL_INTERNALFORMAT_BLUE_SIZE, sizeof(GLint), &data[2]);
|
|
GL.glGetInternalformativ(target, fmt, eGL_INTERNALFORMAT_ALPHA_SIZE, sizeof(GLint), &data[3]);
|
|
|
|
ret.compCount = 0;
|
|
for(int i = 0; i < 4; i++)
|
|
if(data[i] > 0)
|
|
ret.compCount++;
|
|
|
|
for(int i = ret.compCount; i < 4; i++)
|
|
data[i] = data[0];
|
|
|
|
if(data[0] == data[1] && data[1] == data[2] && data[2] == data[3])
|
|
{
|
|
ret.compByteWidth = (uint8_t)(data[0] / 8);
|
|
|
|
// wasn't a byte format (8, 16, 32)
|
|
if(int32_t(ret.compByteWidth) * 8 != data[0])
|
|
{
|
|
ret.type = ResourceFormatType::Undefined;
|
|
RDCERR("Unexpected/unhandled non-uniform format: '%s'", ToStr(fmt).c_str());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret.type = ResourceFormatType::Undefined;
|
|
RDCERR("Unexpected/unhandled non-uniform format: '%s'", ToStr(fmt).c_str());
|
|
}
|
|
|
|
GL.glGetInternalformativ(target, fmt, eGL_INTERNALFORMAT_RED_TYPE, sizeof(GLint), &data[0]);
|
|
GL.glGetInternalformativ(target, fmt, eGL_INTERNALFORMAT_GREEN_TYPE, sizeof(GLint), &data[1]);
|
|
GL.glGetInternalformativ(target, fmt, eGL_INTERNALFORMAT_BLUE_TYPE, sizeof(GLint), &data[2]);
|
|
GL.glGetInternalformativ(target, fmt, eGL_INTERNALFORMAT_ALPHA_TYPE, sizeof(GLint), &data[3]);
|
|
|
|
for(int i = ret.compCount; i < 4; i++)
|
|
data[i] = data[0];
|
|
|
|
if(data[0] == data[1] && data[1] == data[2] && data[2] == data[3])
|
|
{
|
|
switch(edata[0])
|
|
{
|
|
case eGL_UNSIGNED_INT: ret.compType = CompType::UInt; break;
|
|
case eGL_UNSIGNED_BYTE:
|
|
case eGL_UNSIGNED_NORMALIZED: ret.compType = CompType::UNorm; break;
|
|
case eGL_SIGNED_NORMALIZED: ret.compType = CompType::SNorm; break;
|
|
case eGL_FLOAT: ret.compType = CompType::Float; break;
|
|
case eGL_INT: ret.compType = CompType::SInt; break;
|
|
default: RDCERR("Unexpected texture type");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret.type = ResourceFormatType::Undefined;
|
|
RDCERR("Unexpected/unhandled non-uniform format: '%s'", ToStr(fmt).c_str());
|
|
}
|
|
|
|
GL.glGetInternalformativ(target, fmt, eGL_COLOR_ENCODING, sizeof(GLint), &data[0]);
|
|
if(edata[0] == eGL_SRGB || fmt == eGL_SR8_EXT || fmt == eGL_SRG8_EXT)
|
|
ret.compType = CompType::UNormSRGB;
|
|
}
|
|
else if(isdepth == GL_TRUE || isstencil == GL_TRUE)
|
|
{
|
|
// depth format
|
|
ret.compType = CompType::Depth;
|
|
|
|
switch(fmt)
|
|
{
|
|
case eGL_DEPTH_COMPONENT16:
|
|
ret.compByteWidth = 2;
|
|
ret.compCount = 1;
|
|
break;
|
|
case eGL_DEPTH_COMPONENT24:
|
|
ret.compByteWidth = 3;
|
|
ret.compCount = 1;
|
|
break;
|
|
case eGL_DEPTH_COMPONENT:
|
|
case eGL_DEPTH_COMPONENT32:
|
|
case eGL_DEPTH_COMPONENT32F:
|
|
ret.compByteWidth = 4;
|
|
ret.compCount = 1;
|
|
break;
|
|
case eGL_DEPTH24_STENCIL8:
|
|
ret.compByteWidth = 0;
|
|
ret.compCount = 2;
|
|
ret.type = ResourceFormatType::D24S8;
|
|
break;
|
|
case eGL_DEPTH_STENCIL:
|
|
case eGL_DEPTH32F_STENCIL8:
|
|
ret.compByteWidth = 0;
|
|
ret.compCount = 2;
|
|
ret.type = ResourceFormatType::D32S8;
|
|
break;
|
|
case eGL_STENCIL_INDEX:
|
|
case eGL_STENCIL_INDEX8:
|
|
ret.compByteWidth = 1;
|
|
ret.compCount = 1;
|
|
ret.type = ResourceFormatType::S8;
|
|
break;
|
|
default: RDCERR("Unexpected depth or stencil format '%s'", ToStr(fmt).c_str());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// not colour or depth!
|
|
RDCERR("Unexpected texture type, not colour or depth: '%s'", ToStr(fmt).c_str());
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
GLenum MakeGLFormat(ResourceFormat fmt)
|
|
{
|
|
GLenum ret = eGL_NONE;
|
|
|
|
if(fmt.Special())
|
|
{
|
|
switch(fmt.type)
|
|
{
|
|
case ResourceFormatType::BC1:
|
|
{
|
|
if(fmt.compCount == 3)
|
|
ret = fmt.SRGBCorrected() ? eGL_COMPRESSED_SRGB_S3TC_DXT1_EXT
|
|
: eGL_COMPRESSED_RGB_S3TC_DXT1_EXT;
|
|
else
|
|
ret = fmt.SRGBCorrected() ? eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT
|
|
: eGL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
|
break;
|
|
}
|
|
case ResourceFormatType::BC2:
|
|
ret = fmt.SRGBCorrected() ? eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT
|
|
: eGL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
|
break;
|
|
case ResourceFormatType::BC3:
|
|
ret = fmt.SRGBCorrected() ? eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT
|
|
: eGL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
|
break;
|
|
case ResourceFormatType::BC4:
|
|
ret = fmt.compType == CompType::SNorm ? eGL_COMPRESSED_SIGNED_RED_RGTC1
|
|
: eGL_COMPRESSED_RED_RGTC1;
|
|
break;
|
|
case ResourceFormatType::BC5:
|
|
ret = fmt.compType == CompType::SNorm ? eGL_COMPRESSED_SIGNED_RG_RGTC2
|
|
: eGL_COMPRESSED_RG_RGTC2;
|
|
break;
|
|
case ResourceFormatType::BC6:
|
|
ret = fmt.compType == CompType::SNorm ? eGL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB
|
|
: eGL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB;
|
|
break;
|
|
case ResourceFormatType::BC7:
|
|
ret = fmt.SRGBCorrected() ? eGL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB
|
|
: eGL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
|
|
break;
|
|
case ResourceFormatType::ETC2:
|
|
{
|
|
if(fmt.compCount == 3)
|
|
ret = fmt.SRGBCorrected() ? eGL_COMPRESSED_SRGB8_ETC2 : eGL_COMPRESSED_RGB8_ETC2;
|
|
else
|
|
ret = fmt.SRGBCorrected() ? eGL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2
|
|
: eGL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2;
|
|
break;
|
|
}
|
|
case ResourceFormatType::EAC:
|
|
{
|
|
if(fmt.compCount == 1)
|
|
ret = fmt.compType == CompType::SNorm ? eGL_COMPRESSED_SIGNED_R11_EAC
|
|
: eGL_COMPRESSED_R11_EAC;
|
|
else if(fmt.compCount == 2)
|
|
ret = fmt.compType == CompType::SNorm ? eGL_COMPRESSED_SIGNED_RG11_EAC
|
|
: eGL_COMPRESSED_RG11_EAC;
|
|
else
|
|
ret = fmt.SRGBCorrected() ? eGL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC
|
|
: eGL_COMPRESSED_RGBA8_ETC2_EAC;
|
|
break;
|
|
}
|
|
case ResourceFormatType::R10G10B10A2:
|
|
if(fmt.compType == CompType::UNorm)
|
|
ret = eGL_RGB10_A2;
|
|
else
|
|
ret = eGL_RGB10_A2UI;
|
|
break;
|
|
case ResourceFormatType::R11G11B10: ret = eGL_R11F_G11F_B10F; break;
|
|
case ResourceFormatType::R5G6B5: ret = eGL_RGB565; break;
|
|
case ResourceFormatType::R5G5B5A1: ret = eGL_RGB5_A1; break;
|
|
case ResourceFormatType::R9G9B9E5: ret = eGL_RGB9_E5; break;
|
|
case ResourceFormatType::R4G4B4A4: ret = eGL_RGBA4; break;
|
|
case ResourceFormatType::D24S8: ret = eGL_DEPTH24_STENCIL8; break;
|
|
case ResourceFormatType::D32S8: ret = eGL_DEPTH32F_STENCIL8; break;
|
|
case ResourceFormatType::D16S8: return eGL_NONE;
|
|
case ResourceFormatType::ASTC:
|
|
RDCWARN("ASTC can't be decoded unambiguously");
|
|
return eGL_NONE;
|
|
case ResourceFormatType::PVRTC:
|
|
RDCWARN("PVRTC can't be decoded unambiguously");
|
|
return eGL_NONE;
|
|
case ResourceFormatType::S8: ret = eGL_STENCIL_INDEX8; break;
|
|
case ResourceFormatType::A8: ret = eGL_ALPHA8_EXT; break;
|
|
case ResourceFormatType::Undefined: return eGL_NONE;
|
|
default: RDCERR("Unsupported resource format type %u", fmt.type); break;
|
|
}
|
|
}
|
|
else if(fmt.compCount == 4)
|
|
{
|
|
if(fmt.SRGBCorrected())
|
|
{
|
|
ret = eGL_SRGB8_ALPHA8;
|
|
}
|
|
else if(fmt.BGRAOrder())
|
|
{
|
|
ret = eGL_BGRA8_EXT;
|
|
}
|
|
else if(fmt.compByteWidth == 4)
|
|
{
|
|
if(fmt.compType == CompType::Float)
|
|
ret = eGL_RGBA32F;
|
|
else if(fmt.compType == CompType::SInt)
|
|
ret = eGL_RGBA32I;
|
|
else if(fmt.compType == CompType::UInt || fmt.compType == CompType::Typeless)
|
|
ret = eGL_RGBA32UI;
|
|
else
|
|
RDCERR("Unrecognised component type");
|
|
}
|
|
else if(fmt.compByteWidth == 2)
|
|
{
|
|
if(fmt.compType == CompType::Float)
|
|
ret = eGL_RGBA16F;
|
|
else if(fmt.compType == CompType::SInt)
|
|
ret = eGL_RGBA16I;
|
|
else if(fmt.compType == CompType::UInt || fmt.compType == CompType::Typeless)
|
|
ret = eGL_RGBA16UI;
|
|
else if(fmt.compType == CompType::SNorm)
|
|
ret = eGL_RGBA16_SNORM;
|
|
else if(fmt.compType == CompType::UNorm)
|
|
ret = eGL_RGBA16;
|
|
else
|
|
RDCERR("Unrecognised component type");
|
|
}
|
|
else if(fmt.compByteWidth == 1)
|
|
{
|
|
if(fmt.compType == CompType::SInt)
|
|
ret = eGL_RGBA8I;
|
|
else if(fmt.compType == CompType::UInt || fmt.compType == CompType::Typeless)
|
|
ret = eGL_RGBA8UI;
|
|
else if(fmt.compType == CompType::SNorm)
|
|
ret = eGL_RGBA8_SNORM;
|
|
else if(fmt.compType == CompType::UNorm)
|
|
ret = eGL_RGBA8;
|
|
else
|
|
RDCERR("Unrecognised component type");
|
|
}
|
|
else
|
|
{
|
|
RDCERR("Unrecognised 4-component byte width: %d", fmt.compByteWidth);
|
|
}
|
|
}
|
|
else if(fmt.compCount == 3)
|
|
{
|
|
if(fmt.SRGBCorrected())
|
|
{
|
|
ret = eGL_SRGB8;
|
|
}
|
|
else if(fmt.compByteWidth == 4)
|
|
{
|
|
if(fmt.compType == CompType::Float)
|
|
ret = eGL_RGB32F;
|
|
else if(fmt.compType == CompType::SInt)
|
|
ret = eGL_RGB32I;
|
|
else if(fmt.compType == CompType::UInt || fmt.compType == CompType::Typeless)
|
|
ret = eGL_RGB32UI;
|
|
else
|
|
RDCERR("Unrecognised component type");
|
|
}
|
|
else if(fmt.compByteWidth == 2)
|
|
{
|
|
if(fmt.compType == CompType::Float)
|
|
ret = eGL_RGB16F;
|
|
else if(fmt.compType == CompType::SInt)
|
|
ret = eGL_RGB16I;
|
|
else if(fmt.compType == CompType::UInt || fmt.compType == CompType::Typeless)
|
|
ret = eGL_RGB16UI;
|
|
else if(fmt.compType == CompType::SNorm)
|
|
ret = eGL_RGB16_SNORM;
|
|
else if(fmt.compType == CompType::UNorm)
|
|
ret = eGL_RGB16;
|
|
else
|
|
RDCERR("Unrecognised component type");
|
|
}
|
|
else if(fmt.compByteWidth == 1)
|
|
{
|
|
if(fmt.compType == CompType::SInt)
|
|
ret = eGL_RGB8I;
|
|
else if(fmt.compType == CompType::UInt || fmt.compType == CompType::Typeless)
|
|
ret = eGL_RGB8UI;
|
|
else if(fmt.compType == CompType::SNorm)
|
|
ret = eGL_RGB8_SNORM;
|
|
else if(fmt.compType == CompType::UNorm)
|
|
ret = eGL_RGB8;
|
|
else
|
|
RDCERR("Unrecognised component type");
|
|
}
|
|
else
|
|
{
|
|
RDCERR("Unrecognised 3-component byte width: %d", fmt.compByteWidth);
|
|
}
|
|
}
|
|
else if(fmt.compCount == 2)
|
|
{
|
|
if(fmt.SRGBCorrected())
|
|
{
|
|
ret = eGL_SRG8_EXT;
|
|
}
|
|
else if(fmt.compByteWidth == 4)
|
|
{
|
|
if(fmt.compType == CompType::Float)
|
|
ret = eGL_RG32F;
|
|
else if(fmt.compType == CompType::SInt)
|
|
ret = eGL_RG32I;
|
|
else if(fmt.compType == CompType::UInt || fmt.compType == CompType::Typeless)
|
|
ret = eGL_RG32UI;
|
|
else
|
|
RDCERR("Unrecognised component type");
|
|
}
|
|
else if(fmt.compByteWidth == 2)
|
|
{
|
|
if(fmt.compType == CompType::Float)
|
|
ret = eGL_RG16F;
|
|
else if(fmt.compType == CompType::SInt)
|
|
ret = eGL_RG16I;
|
|
else if(fmt.compType == CompType::UInt || fmt.compType == CompType::Typeless)
|
|
ret = eGL_RG16UI;
|
|
else if(fmt.compType == CompType::SNorm)
|
|
ret = eGL_RG16_SNORM;
|
|
else if(fmt.compType == CompType::UNorm)
|
|
ret = eGL_RG16;
|
|
else
|
|
RDCERR("Unrecognised component type");
|
|
}
|
|
else if(fmt.compByteWidth == 1)
|
|
{
|
|
if(fmt.compType == CompType::SInt)
|
|
ret = eGL_RG8I;
|
|
else if(fmt.compType == CompType::UInt || fmt.compType == CompType::Typeless)
|
|
ret = eGL_RG8UI;
|
|
else if(fmt.compType == CompType::SNorm)
|
|
ret = eGL_RG8_SNORM;
|
|
else if(fmt.compType == CompType::UNorm)
|
|
ret = eGL_RG8;
|
|
else
|
|
RDCERR("Unrecognised component type");
|
|
}
|
|
else
|
|
{
|
|
RDCERR("Unrecognised 3-component byte width: %d", fmt.compByteWidth);
|
|
}
|
|
}
|
|
else if(fmt.compCount == 1)
|
|
{
|
|
if(fmt.SRGBCorrected())
|
|
{
|
|
ret = eGL_SR8_EXT;
|
|
}
|
|
else if(fmt.compByteWidth == 4)
|
|
{
|
|
if(fmt.compType == CompType::Float)
|
|
ret = eGL_R32F;
|
|
else if(fmt.compType == CompType::SInt)
|
|
ret = eGL_R32I;
|
|
else if(fmt.compType == CompType::UInt || fmt.compType == CompType::Typeless)
|
|
ret = eGL_R32UI;
|
|
else if(fmt.compType == CompType::Depth)
|
|
ret = eGL_DEPTH_COMPONENT32F;
|
|
else
|
|
RDCERR("Unrecognised component type");
|
|
}
|
|
else if(fmt.compByteWidth == 3)
|
|
{
|
|
ret = eGL_DEPTH_COMPONENT24;
|
|
}
|
|
else if(fmt.compByteWidth == 2)
|
|
{
|
|
if(fmt.compType == CompType::Float)
|
|
ret = eGL_R16F;
|
|
else if(fmt.compType == CompType::SInt)
|
|
ret = eGL_R16I;
|
|
else if(fmt.compType == CompType::UInt || fmt.compType == CompType::Typeless)
|
|
ret = eGL_R16UI;
|
|
else if(fmt.compType == CompType::SNorm)
|
|
ret = eGL_R16_SNORM;
|
|
else if(fmt.compType == CompType::UNorm)
|
|
ret = eGL_R16;
|
|
else if(fmt.compType == CompType::Depth)
|
|
ret = eGL_DEPTH_COMPONENT16;
|
|
else
|
|
RDCERR("Unrecognised component type");
|
|
}
|
|
else if(fmt.compByteWidth == 1)
|
|
{
|
|
if(fmt.compType == CompType::SInt)
|
|
ret = eGL_R8I;
|
|
else if(fmt.compType == CompType::UInt || fmt.compType == CompType::Typeless)
|
|
ret = eGL_R8UI;
|
|
else if(fmt.compType == CompType::SNorm)
|
|
ret = eGL_R8_SNORM;
|
|
else if(fmt.compType == CompType::UNorm)
|
|
ret = eGL_R8;
|
|
else
|
|
RDCERR("Unrecognised component type");
|
|
}
|
|
else
|
|
{
|
|
RDCERR("Unrecognised 3-component byte width: %d", fmt.compByteWidth);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RDCERR("Unrecognised component count: %d", fmt.compCount);
|
|
}
|
|
|
|
if(ret == eGL_NONE)
|
|
RDCERR("No known GL format corresponding to resource format!");
|
|
|
|
return ret;
|
|
}
|
|
|
|
GLenum MakeGLPrimitiveTopology(Topology Topo)
|
|
{
|
|
switch(Topo)
|
|
{
|
|
default: return eGL_NONE;
|
|
case Topology::PointList: return eGL_POINTS;
|
|
case Topology::LineStrip: return eGL_LINE_STRIP;
|
|
case Topology::LineLoop: return eGL_LINE_LOOP;
|
|
case Topology::LineList: return eGL_LINES;
|
|
case Topology::LineStrip_Adj: return eGL_LINE_STRIP_ADJACENCY;
|
|
case Topology::LineList_Adj: return eGL_LINES_ADJACENCY;
|
|
case Topology::TriangleStrip: return eGL_TRIANGLE_STRIP;
|
|
case Topology::TriangleFan: return eGL_TRIANGLE_FAN;
|
|
case Topology::TriangleList: return eGL_TRIANGLES;
|
|
case Topology::TriangleStrip_Adj: return eGL_TRIANGLE_STRIP_ADJACENCY;
|
|
case Topology::TriangleList_Adj: return eGL_TRIANGLES_ADJACENCY;
|
|
case Topology::PatchList_1CPs:
|
|
case Topology::PatchList_2CPs:
|
|
case Topology::PatchList_3CPs:
|
|
case Topology::PatchList_4CPs:
|
|
case Topology::PatchList_5CPs:
|
|
case Topology::PatchList_6CPs:
|
|
case Topology::PatchList_7CPs:
|
|
case Topology::PatchList_8CPs:
|
|
case Topology::PatchList_9CPs:
|
|
case Topology::PatchList_10CPs:
|
|
case Topology::PatchList_11CPs:
|
|
case Topology::PatchList_12CPs:
|
|
case Topology::PatchList_13CPs:
|
|
case Topology::PatchList_14CPs:
|
|
case Topology::PatchList_15CPs:
|
|
case Topology::PatchList_16CPs:
|
|
case Topology::PatchList_17CPs:
|
|
case Topology::PatchList_18CPs:
|
|
case Topology::PatchList_19CPs:
|
|
case Topology::PatchList_20CPs:
|
|
case Topology::PatchList_21CPs:
|
|
case Topology::PatchList_22CPs:
|
|
case Topology::PatchList_23CPs:
|
|
case Topology::PatchList_24CPs:
|
|
case Topology::PatchList_25CPs:
|
|
case Topology::PatchList_26CPs:
|
|
case Topology::PatchList_27CPs:
|
|
case Topology::PatchList_28CPs:
|
|
case Topology::PatchList_29CPs:
|
|
case Topology::PatchList_30CPs:
|
|
case Topology::PatchList_31CPs:
|
|
case Topology::PatchList_32CPs: return eGL_PATCHES;
|
|
}
|
|
}
|
|
|
|
Topology MakePrimitiveTopology(GLenum Topo)
|
|
{
|
|
switch(Topo)
|
|
{
|
|
default: return Topology::Unknown;
|
|
case eGL_POINTS: return Topology::PointList;
|
|
case eGL_LINE_STRIP: return Topology::LineStrip;
|
|
case eGL_LINE_LOOP: return Topology::LineLoop;
|
|
case eGL_LINES: return Topology::LineList;
|
|
case eGL_LINE_STRIP_ADJACENCY: return Topology::LineStrip_Adj;
|
|
case eGL_LINES_ADJACENCY: return Topology::LineList_Adj;
|
|
case eGL_TRIANGLE_STRIP: return Topology::TriangleStrip;
|
|
case eGL_TRIANGLE_FAN: return Topology::TriangleFan;
|
|
case eGL_TRIANGLES: return Topology::TriangleList;
|
|
case eGL_TRIANGLE_STRIP_ADJACENCY: return Topology::TriangleStrip_Adj;
|
|
case eGL_TRIANGLES_ADJACENCY: return Topology::TriangleList_Adj;
|
|
case eGL_PATCHES:
|
|
{
|
|
GLint patchCount = 3;
|
|
GL.glGetIntegerv(eGL_PATCH_VERTICES, &patchCount);
|
|
return PatchList_Topology(patchCount);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if ENABLED(ENABLE_UNIT_TESTS)
|
|
|
|
#undef None
|
|
|
|
#include "3rdparty/catch/catch.hpp"
|
|
|
|
TEST_CASE("GL formats", "[format][gl]")
|
|
{
|
|
// must be updated by hand
|
|
GLenum supportedFormats[] = {
|
|
eGL_NONE,
|
|
eGL_R8,
|
|
eGL_R8_SNORM,
|
|
eGL_R8UI,
|
|
eGL_R8I,
|
|
eGL_SR8_EXT,
|
|
eGL_RG8,
|
|
eGL_RG8_SNORM,
|
|
eGL_RG8UI,
|
|
eGL_RG8I,
|
|
eGL_SRG8_EXT,
|
|
eGL_RGB8,
|
|
eGL_RGB8_SNORM,
|
|
eGL_RGB8UI,
|
|
eGL_RGB8I,
|
|
eGL_SRGB8,
|
|
eGL_RGBA8,
|
|
eGL_RGBA8_SNORM,
|
|
eGL_RGBA8UI,
|
|
eGL_RGBA8I,
|
|
eGL_SRGB8_ALPHA8,
|
|
eGL_BGRA8_EXT,
|
|
eGL_R16,
|
|
eGL_R16_SNORM,
|
|
eGL_R16UI,
|
|
eGL_R16I,
|
|
eGL_R16F,
|
|
eGL_RG16,
|
|
eGL_RG16_SNORM,
|
|
eGL_RG16UI,
|
|
eGL_RG16I,
|
|
eGL_RG16F,
|
|
eGL_RGB16,
|
|
eGL_RGB16_SNORM,
|
|
eGL_RGB16UI,
|
|
eGL_RGB16I,
|
|
eGL_RGB16F,
|
|
eGL_RGBA16,
|
|
eGL_RGBA16_SNORM,
|
|
eGL_RGBA16UI,
|
|
eGL_RGBA16I,
|
|
eGL_RGBA16F,
|
|
eGL_R32UI,
|
|
eGL_R32I,
|
|
eGL_R32F,
|
|
eGL_RG32UI,
|
|
eGL_RG32I,
|
|
eGL_RG32F,
|
|
eGL_RGB32UI,
|
|
eGL_RGB32I,
|
|
eGL_RGB32F,
|
|
eGL_RGBA32UI,
|
|
eGL_RGBA32I,
|
|
eGL_RGBA32F,
|
|
eGL_RGBA4,
|
|
eGL_RGB565,
|
|
eGL_RGB5_A1,
|
|
eGL_R11F_G11F_B10F,
|
|
eGL_RGB9_E5,
|
|
eGL_RGB10_A2,
|
|
eGL_RGB10_A2UI,
|
|
eGL_DEPTH_COMPONENT16,
|
|
eGL_DEPTH_COMPONENT24,
|
|
eGL_DEPTH_COMPONENT32,
|
|
eGL_DEPTH_COMPONENT32F,
|
|
eGL_DEPTH24_STENCIL8,
|
|
eGL_DEPTH32F_STENCIL8,
|
|
eGL_COMPRESSED_RGB_S3TC_DXT1_EXT,
|
|
eGL_COMPRESSED_SRGB_S3TC_DXT1_EXT,
|
|
eGL_COMPRESSED_RGBA_S3TC_DXT1_EXT,
|
|
eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT,
|
|
eGL_COMPRESSED_RGBA_S3TC_DXT3_EXT,
|
|
eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT,
|
|
eGL_COMPRESSED_RGBA_S3TC_DXT5_EXT,
|
|
eGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT,
|
|
eGL_COMPRESSED_RED_RGTC1,
|
|
eGL_COMPRESSED_SIGNED_RED_RGTC1,
|
|
eGL_COMPRESSED_RG_RGTC2,
|
|
eGL_COMPRESSED_SIGNED_RG_RGTC2,
|
|
eGL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB,
|
|
eGL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB,
|
|
eGL_COMPRESSED_RGBA_BPTC_UNORM_ARB,
|
|
eGL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB,
|
|
eGL_ETC1_RGB8_OES,
|
|
eGL_COMPRESSED_RGB8_ETC2,
|
|
eGL_COMPRESSED_SRGB8_ETC2,
|
|
eGL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2,
|
|
eGL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2,
|
|
eGL_COMPRESSED_RGBA8_ETC2_EAC,
|
|
eGL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC,
|
|
eGL_COMPRESSED_R11_EAC,
|
|
eGL_COMPRESSED_SIGNED_R11_EAC,
|
|
eGL_COMPRESSED_RG11_EAC,
|
|
eGL_COMPRESSED_SIGNED_RG11_EAC,
|
|
eGL_COMPRESSED_RGBA_ASTC_4x4_KHR,
|
|
eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR,
|
|
eGL_COMPRESSED_RGBA_ASTC_5x4_KHR,
|
|
eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR,
|
|
eGL_COMPRESSED_RGBA_ASTC_5x5_KHR,
|
|
eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR,
|
|
eGL_COMPRESSED_RGBA_ASTC_6x5_KHR,
|
|
eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR,
|
|
eGL_COMPRESSED_RGBA_ASTC_6x6_KHR,
|
|
eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR,
|
|
eGL_COMPRESSED_RGBA_ASTC_8x5_KHR,
|
|
eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR,
|
|
eGL_COMPRESSED_RGBA_ASTC_8x6_KHR,
|
|
eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR,
|
|
eGL_COMPRESSED_RGBA_ASTC_8x8_KHR,
|
|
eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR,
|
|
eGL_COMPRESSED_RGBA_ASTC_10x5_KHR,
|
|
eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR,
|
|
eGL_COMPRESSED_RGBA_ASTC_10x6_KHR,
|
|
eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR,
|
|
eGL_COMPRESSED_RGBA_ASTC_10x8_KHR,
|
|
eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR,
|
|
eGL_COMPRESSED_RGBA_ASTC_10x10_KHR,
|
|
eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR,
|
|
eGL_COMPRESSED_RGBA_ASTC_12x10_KHR,
|
|
eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR,
|
|
eGL_COMPRESSED_RGBA_ASTC_12x12_KHR,
|
|
eGL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR,
|
|
eGL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT,
|
|
eGL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT,
|
|
eGL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT,
|
|
eGL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT,
|
|
};
|
|
|
|
// we use our emulated queries for the format, as we don't want to init a context here, and anyway
|
|
// we'd rather have an isolated test-case of only our code, not be testing a GL driver
|
|
// implementation
|
|
GL.EmulateRequiredExtensions();
|
|
|
|
SECTION("Only GL_NONE returns unknown")
|
|
{
|
|
for(GLenum f : supportedFormats)
|
|
{
|
|
ResourceFormat fmt = MakeResourceFormat(eGL_TEXTURE_2D, f);
|
|
|
|
if(f == eGL_NONE)
|
|
CHECK(fmt.type == ResourceFormatType::Undefined);
|
|
else
|
|
CHECK(fmt.type != ResourceFormatType::Undefined);
|
|
}
|
|
};
|
|
|
|
SECTION("MakeGLFormat is reflexive with MakeResourceFormat")
|
|
{
|
|
for(GLenum f : supportedFormats)
|
|
{
|
|
// we don't support ETC1
|
|
if(f == GL_ETC1_RGB8_OES)
|
|
continue;
|
|
|
|
ResourceFormat fmt = MakeResourceFormat(eGL_TEXTURE_2D, f);
|
|
|
|
// we don't support ASTC/PVRTC formats currently
|
|
if(fmt.type == ResourceFormatType::ASTC || fmt.type == ResourceFormatType::PVRTC)
|
|
continue;
|
|
|
|
GLenum glf = MakeGLFormat(fmt);
|
|
|
|
// it's OK to 'lose' the non-float flag on this format
|
|
if(f == eGL_DEPTH_COMPONENT32)
|
|
{
|
|
CHECK(glf == eGL_DEPTH_COMPONENT32F);
|
|
}
|
|
else
|
|
{
|
|
CHECK(glf == f);
|
|
}
|
|
}
|
|
};
|
|
|
|
SECTION("GetByteSize and GetFormatBPP return expected values for regular formats")
|
|
{
|
|
for(GLenum f : supportedFormats)
|
|
{
|
|
ResourceFormat fmt = MakeResourceFormat(eGL_TEXTURE_2D, f);
|
|
|
|
if(fmt.type != ResourceFormatType::Regular)
|
|
continue;
|
|
|
|
INFO("Format is " << ToStr(f));
|
|
|
|
uint32_t size = fmt.compCount * fmt.compByteWidth * 123 * 456;
|
|
|
|
// this takes up a full int, even if the byte width is listed as 3.
|
|
if(f == eGL_DEPTH_COMPONENT24)
|
|
size = fmt.compCount * 4 * 123 * 456;
|
|
|
|
CHECK(size == GetByteSize(123, 456, 1, GetBaseFormat(f), GetDataType(f)));
|
|
}
|
|
};
|
|
|
|
GL = GLDispatchTable();
|
|
};
|
|
|
|
#endif // ENABLED(ENABLE_UNIT_TESTS)
|