CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Common/GPU/OpenGL/GLQueueRunner.cpp
Views: 1401
#include "ppsspp_config.h"1#include <algorithm>23#include "Common/GPU/OpenGL/GLCommon.h"4#include "Common/GPU/OpenGL/GLDebugLog.h"5#include "Common/GPU/OpenGL/GLFeatures.h"6#include "Common/GPU/OpenGL/DataFormatGL.h"7#include "Common/Math/math_util.h"8#include "Common/VR/PPSSPPVR.h"910#include "Common/Log.h"11#include "Common/LogReporting.h"12#include "Common/MemoryUtil.h"13#include "Common/StringUtils.h"14#include "Common/Data/Convert/SmallDataConvert.h"1516#include "GLQueueRunner.h"17#include "GLRenderManager.h"18#include "DataFormatGL.h"1920// These are the same value, alias for simplicity.21#if defined(GL_CLIP_DISTANCE0_EXT) && !defined(GL_CLIP_DISTANCE0)22#define GL_CLIP_DISTANCE0 GL_CLIP_DISTANCE0_EXT23#elif !defined(GL_CLIP_DISTANCE0)24#define GL_CLIP_DISTANCE0 0x300025#endif26#ifndef GL_DEPTH_STENCIL_TEXTURE_MODE27#define GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA28#endif29#ifndef GL_STENCIL_INDEX30#define GL_STENCIL_INDEX 0x190131#endif3233static constexpr int TEXCACHE_NAME_CACHE_SIZE = 16;3435#if PPSSPP_PLATFORM(IOS)36extern void bindDefaultFBO();37#endif3839// Workaround for Retroarch. Simply declare40// extern GLuint g_defaultFBO;41// and set is as appropriate. Can adjust the variables in ext/native/base/display.h as42// appropriate.43GLuint g_defaultFBO = 0;4445void GLQueueRunner::CreateDeviceObjects() {46CHECK_GL_ERROR_IF_DEBUG();47if (caps_.anisoSupported) {48glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropyLevel_);49} else {50maxAnisotropyLevel_ = 0.0f;51}5253if (gl_extensions.ARB_vertex_array_object) {54glGenVertexArrays(1, &globalVAO_);55}5657// An eternal optimist.58sawOutOfMemory_ = false;5960// Populate some strings from the GL thread so they can be queried from thin3d.61// TODO: Merge with GLFeatures.cpp/h62auto populate = [&](int name) {63const GLubyte *value = glGetString(name);64if (!value)65glStrings_[name] = "?";66else67glStrings_[name] = (const char *)value;68};69populate(GL_VENDOR);70populate(GL_RENDERER);71populate(GL_VERSION);72populate(GL_SHADING_LANGUAGE_VERSION);73CHECK_GL_ERROR_IF_DEBUG();7475#if !PPSSPP_ARCH(X86) // Doesn't work on AMD for some reason. See issue #1778776useDebugGroups_ = !gl_extensions.IsGLES && gl_extensions.VersionGEThan(4, 3);77#endif78}7980void GLQueueRunner::DestroyDeviceObjects() {81CHECK_GL_ERROR_IF_DEBUG();82if (gl_extensions.ARB_vertex_array_object) {83glDeleteVertexArrays(1, &globalVAO_);84}85delete[] readbackBuffer_;86readbackBuffer_ = nullptr;87readbackBufferSize_ = 0;88CHECK_GL_ERROR_IF_DEBUG();89}9091template <typename Getiv, typename GetLog>92static std::string GetInfoLog(GLuint name, Getiv getiv, GetLog getLog) {93GLint bufLength = 0;94getiv(name, GL_INFO_LOG_LENGTH, &bufLength);95if (bufLength <= 0)96bufLength = 2048;9798std::string infoLog;99infoLog.resize(bufLength);100GLsizei len = 0;101getLog(name, (GLsizei)infoLog.size(), &len, &infoLog[0]);102if (len <= 0)103return "(unknown reason)";104105infoLog.resize(len);106return infoLog;107}108109void GLQueueRunner::RunInitSteps(const FastVec<GLRInitStep> &steps, bool skipGLCalls) {110if (skipGLCalls) {111// Some bookkeeping still needs to be done.112for (size_t i = 0; i < steps.size(); i++) {113const GLRInitStep &step = steps[i];114switch (step.stepType) {115case GLRInitStepType::BUFFER_SUBDATA:116{117if (step.buffer_subdata.deleteData)118delete[] step.buffer_subdata.data;119break;120}121case GLRInitStepType::TEXTURE_IMAGE:122{123if (step.texture_image.allocType == GLRAllocType::ALIGNED) {124FreeAlignedMemory(step.texture_image.data);125} else if (step.texture_image.allocType == GLRAllocType::NEW) {126delete[] step.texture_image.data;127}128break;129}130case GLRInitStepType::CREATE_PROGRAM:131{132WARN_LOG(Log::G3D, "CREATE_PROGRAM found with skipGLCalls, not good");133break;134}135case GLRInitStepType::CREATE_SHADER:136{137WARN_LOG(Log::G3D, "CREATE_SHADER found with skipGLCalls, not good");138break;139}140default:141break;142}143}144return;145}146147#if !defined(USING_GLES2)148if (useDebugGroups_)149glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 1, -1, "InitSteps");150#endif151152CHECK_GL_ERROR_IF_DEBUG();153glActiveTexture(GL_TEXTURE0);154GLuint boundTexture = (GLuint)-1;155bool allocatedTextures = false;156157for (size_t i = 0; i < steps.size(); i++) {158const GLRInitStep &step = steps[i];159switch (step.stepType) {160case GLRInitStepType::CREATE_TEXTURE:161{162GLRTexture *tex = step.create_texture.texture;163glGenTextures(1, &tex->texture);164glBindTexture(tex->target, tex->texture);165boundTexture = tex->texture;166CHECK_GL_ERROR_IF_DEBUG();167break;168}169case GLRInitStepType::CREATE_BUFFER:170{171GLRBuffer *buffer = step.create_buffer.buffer;172glGenBuffers(1, &buffer->buffer_);173glBindBuffer(buffer->target_, buffer->buffer_);174glBufferData(buffer->target_, step.create_buffer.size, nullptr, step.create_buffer.usage);175CHECK_GL_ERROR_IF_DEBUG();176break;177}178case GLRInitStepType::BUFFER_SUBDATA:179{180GLRBuffer *buffer = step.buffer_subdata.buffer;181glBindBuffer(buffer->target_, buffer->buffer_);182glBufferSubData(buffer->target_, step.buffer_subdata.offset, step.buffer_subdata.size, step.buffer_subdata.data);183if (step.buffer_subdata.deleteData)184delete[] step.buffer_subdata.data;185CHECK_GL_ERROR_IF_DEBUG();186break;187}188case GLRInitStepType::CREATE_PROGRAM:189{190CHECK_GL_ERROR_IF_DEBUG();191GLRProgram *program = step.create_program.program;192program->program = glCreateProgram();193_assert_msg_(step.create_program.num_shaders > 0, "Can't create a program with zero shaders");194bool anyFailed = false;195for (int j = 0; j < step.create_program.num_shaders; j++) {196_dbg_assert_msg_(step.create_program.shaders[j]->shader, "Can't create a program with a null shader");197anyFailed = anyFailed || step.create_program.shaders[j]->failed;198glAttachShader(program->program, step.create_program.shaders[j]->shader);199}200201for (auto iter : program->semantics_) {202glBindAttribLocation(program->program, iter.location, iter.attrib);203}204205#if !defined(USING_GLES2)206if (step.create_program.support_dual_source) {207_dbg_assert_msg_(caps_.dualSourceBlend, "ARB/EXT_blend_func_extended required for dual src blend");208// Dual source alpha209glBindFragDataLocationIndexed(program->program, 0, 0, "fragColor0");210glBindFragDataLocationIndexed(program->program, 0, 1, "fragColor1");211} else if (gl_extensions.VersionGEThan(3, 0, 0)) {212glBindFragDataLocation(program->program, 0, "fragColor0");213}214#elif !PPSSPP_PLATFORM(IOS)215if (gl_extensions.GLES3 && step.create_program.support_dual_source) {216// For GLES2, we use gl_SecondaryFragColorEXT as fragColor1.217_dbg_assert_msg_(gl_extensions.EXT_blend_func_extended, "EXT_blend_func_extended required for dual src");218glBindFragDataLocationIndexedEXT(program->program, 0, 0, "fragColor0");219glBindFragDataLocationIndexedEXT(program->program, 0, 1, "fragColor1");220}221#endif222glLinkProgram(program->program);223224GLint linkStatus = GL_FALSE;225glGetProgramiv(program->program, GL_LINK_STATUS, &linkStatus);226if (linkStatus != GL_TRUE) {227std::string infoLog = GetInfoLog(program->program, glGetProgramiv, glGetProgramInfoLog);228229// TODO: Could be other than vs/fs. Also, we're assuming order here...230GLRShader *vs = step.create_program.shaders[0];231GLRShader *fs = step.create_program.num_shaders > 1 ? step.create_program.shaders[1] : nullptr;232std::string vsDesc = vs->desc + (vs->failed ? " (failed)" : "");233std::string fsDesc = fs ? (fs->desc + (fs->failed ? " (failed)" : "")) : "(none)";234const char *vsCode = vs->code.c_str();235const char *fsCode = fs ? fs->code.c_str() : "(none)";236if (!anyFailed)237Reporting::ReportMessage("Error in shader program link: info: %s\nfs: %s\n%s\nvs: %s\n%s", infoLog.c_str(), fsDesc.c_str(), fsCode, vsDesc.c_str(), vsCode);238239ERROR_LOG(Log::G3D, "Could not link program:\n %s", infoLog.c_str());240ERROR_LOG(Log::G3D, "VS desc:\n%s", vsDesc.c_str());241ERROR_LOG(Log::G3D, "FS desc:\n%s", fsDesc.c_str());242ERROR_LOG(Log::G3D, "VS:\n%s\n", LineNumberString(vsCode).c_str());243ERROR_LOG(Log::G3D, "FS:\n%s\n", LineNumberString(fsCode).c_str());244245#ifdef _WIN32246OutputDebugStringUTF8(infoLog.c_str());247if (vsCode)248OutputDebugStringUTF8(LineNumberString(vsCode).c_str());249if (fsCode)250OutputDebugStringUTF8(LineNumberString(fsCode).c_str());251#endif252CHECK_GL_ERROR_IF_DEBUG();253break;254}255256glUseProgram(program->program);257258// Query all the uniforms.259for (size_t j = 0; j < program->queries_.size(); j++) {260auto &query = program->queries_[j];261_dbg_assert_(query.name);262263int location = glGetUniformLocation(program->program, query.name);264265if (location < 0 && query.required) {266WARN_LOG(Log::G3D, "Required uniform query for '%s' failed", query.name);267}268*query.dest = location;269}270271// Run initializers.272for (size_t j = 0; j < program->initialize_.size(); j++) {273auto &init = program->initialize_[j];274GLint uniform = *init.uniform;275if (uniform != -1) {276switch (init.type) {277case 0:278glUniform1i(uniform, init.value);279break;280}281}282}283CHECK_GL_ERROR_IF_DEBUG();284break;285}286case GLRInitStepType::CREATE_SHADER:287{288CHECK_GL_ERROR_IF_DEBUG();289GLuint shader = glCreateShader(step.create_shader.stage);290step.create_shader.shader->shader = shader;291const char *code = step.create_shader.code;292glShaderSource(shader, 1, &code, nullptr);293glCompileShader(shader);294GLint success = 0;295glGetShaderiv(shader, GL_COMPILE_STATUS, &success);296if (!success) {297std::string infoLog = GetInfoLog(shader, glGetShaderiv, glGetShaderInfoLog);298std::string errorString = StringFromFormat(299"Error in shader compilation for: %s\n"300"Info log: %s\n"301"Shader source:\n%s\n//END\n\n",302step.create_shader.shader->desc.c_str(),303infoLog.c_str(),304LineNumberString(code).c_str());305std::vector<std::string_view> lines;306SplitString(errorString, '\n', lines);307for (auto line : lines) {308ERROR_LOG(Log::G3D, "%.*s", (int)line.size(), line.data());309}310if (errorCallback_) {311std::string desc = StringFromFormat("Shader compilation failed: %s", step.create_shader.stage == GL_VERTEX_SHADER ? "vertex" : "fragment");312errorCallback_(desc.c_str(), errorString.c_str(), errorCallbackUserData_);313}314Reporting::ReportMessage("Error in shader compilation: info: %s\n%s\n%s", infoLog.c_str(), step.create_shader.shader->desc.c_str(), (const char *)code);315#ifdef SHADERLOG316OutputDebugStringUTF8(infoLog.c_str());317#endif318step.create_shader.shader->failed = true;319step.create_shader.shader->error = infoLog; // Hm, we never use this.320}321// Before we throw away the code, attach it to the shader for debugging.322step.create_shader.shader->code = code;323delete[] step.create_shader.code;324step.create_shader.shader->valid = true;325CHECK_GL_ERROR_IF_DEBUG();326break;327}328case GLRInitStepType::CREATE_INPUT_LAYOUT:329{330// GLRInputLayout *layout = step.create_input_layout.inputLayout;331// Nothing to do unless we want to create vertexbuffer objects (GL 4.5)332break;333}334case GLRInitStepType::CREATE_FRAMEBUFFER:335{336CHECK_GL_ERROR_IF_DEBUG();337boundTexture = (GLuint)-1;338InitCreateFramebuffer(step);339allocatedTextures = true;340CHECK_GL_ERROR_IF_DEBUG();341break;342}343case GLRInitStepType::TEXTURE_IMAGE:344{345GLRTexture *tex = step.texture_image.texture;346CHECK_GL_ERROR_IF_DEBUG();347if (boundTexture != tex->texture) {348glBindTexture(tex->target, tex->texture);349boundTexture = tex->texture;350}351if (!step.texture_image.data && step.texture_image.allocType != GLRAllocType::NONE)352_assert_msg_(false, "missing texture data");353354// For things to show in RenderDoc, need to split into glTexImage2D(..., nullptr) and glTexSubImage.355356int blockSize = 0;357bool bc = Draw::DataFormatIsBlockCompressed(step.texture_image.format, &blockSize);358359GLenum internalFormat, format, type;360int alignment;361Thin3DFormatToGLFormatAndType(step.texture_image.format, internalFormat, format, type, alignment);362if (step.texture_image.depth == 1) {363if (bc) {364int dataSize = ((step.texture_image.width + 3) & ~3) * ((step.texture_image.height + 3) & ~3) * blockSize / 16;365glCompressedTexImage2D(tex->target, step.texture_image.level, internalFormat,366step.texture_image.width, step.texture_image.height, 0, dataSize, step.texture_image.data);367} else {368glTexImage2D(tex->target,369step.texture_image.level, internalFormat,370step.texture_image.width, step.texture_image.height, 0,371format, type, step.texture_image.data);372}373} else {374glTexImage3D(tex->target,375step.texture_image.level, internalFormat,376step.texture_image.width, step.texture_image.height, step.texture_image.depth, 0,377format, type, step.texture_image.data);378}379allocatedTextures = true;380if (step.texture_image.allocType == GLRAllocType::ALIGNED) {381FreeAlignedMemory(step.texture_image.data);382} else if (step.texture_image.allocType == GLRAllocType::NEW) {383delete[] step.texture_image.data;384}385CHECK_GL_ERROR_IF_DEBUG();386tex->wrapS = GL_CLAMP_TO_EDGE;387tex->wrapT = GL_CLAMP_TO_EDGE;388tex->magFilter = step.texture_image.linearFilter ? GL_LINEAR : GL_NEAREST;389tex->minFilter = step.texture_image.linearFilter ? GL_LINEAR : GL_NEAREST;390glTexParameteri(tex->target, GL_TEXTURE_WRAP_S, tex->wrapS);391glTexParameteri(tex->target, GL_TEXTURE_WRAP_T, tex->wrapT);392glTexParameteri(tex->target, GL_TEXTURE_MAG_FILTER, tex->magFilter);393glTexParameteri(tex->target, GL_TEXTURE_MIN_FILTER, tex->minFilter);394if (step.texture_image.depth > 1) {395glTexParameteri(tex->target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);396}397CHECK_GL_ERROR_IF_DEBUG();398break;399}400case GLRInitStepType::TEXTURE_FINALIZE:401{402CHECK_GL_ERROR_IF_DEBUG();403GLRTexture *tex = step.texture_finalize.texture;404if (boundTexture != tex->texture) {405glBindTexture(tex->target, tex->texture);406boundTexture = tex->texture;407}408if ((!gl_extensions.IsGLES || gl_extensions.GLES3) && step.texture_finalize.loadedLevels > 1) {409glTexParameteri(tex->target, GL_TEXTURE_MAX_LEVEL, step.texture_finalize.loadedLevels - 1);410}411tex->maxLod = (float)step.texture_finalize.loadedLevels - 1;412if (step.texture_finalize.genMips) {413glGenerateMipmap(tex->target);414}415CHECK_GL_ERROR_IF_DEBUG();416break;417}418default:419CHECK_GL_ERROR_IF_DEBUG();420_assert_msg_(false, "Bad GLRInitStepType: %d", (int)step.stepType);421break;422}423}424CHECK_GL_ERROR_IF_DEBUG();425426// TODO: Use GL_KHR_no_error or a debug callback, where supported?427if (false && allocatedTextures) {428// Users may use replacements or scaling, with high render resolutions, and run out of VRAM.429// This detects that, rather than looking like PPSSPP is broken.430// Calling glGetError() isn't great, but at the end of init, only after creating textures, shouldn't be too bad...431GLenum err = glGetError();432if (err == GL_OUT_OF_MEMORY) {433WARN_LOG_REPORT(Log::G3D, "GL ran out of GPU memory; switching to low memory mode");434sawOutOfMemory_ = true;435} else if (err != GL_NO_ERROR) {436// We checked the err anyway, might as well log if there is one.437std::string errorString = GLEnumToString(err);438WARN_LOG(Log::G3D, "Got an error after init: %08x (%s)", err, errorString.c_str());439if (errorCallback_) {440errorCallback_("GL frame init error", errorString.c_str(), errorCallbackUserData_);441}442}443}444445glBindBuffer(GL_ARRAY_BUFFER, 0);446glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);447448#if !defined(USING_GLES2)449if (useDebugGroups_)450glPopDebugGroup();451#endif452}453454void GLQueueRunner::InitCreateFramebuffer(const GLRInitStep &step) {455GLRFramebuffer *fbo = step.create_framebuffer.framebuffer;456457#ifndef USING_GLES2458if (!gl_extensions.ARB_framebuffer_object && gl_extensions.EXT_framebuffer_object) {459fbo_ext_create(step);460} else if (!gl_extensions.ARB_framebuffer_object && !gl_extensions.IsGLES) {461return;462}463// If GLES2, we have basic FBO support and can just proceed.464#endif465CHECK_GL_ERROR_IF_DEBUG();466467auto initFBOTexture = [&](GLRTexture &tex, GLint internalFormat, GLenum format, GLenum type, bool linear) {468glGenTextures(1, &tex.texture);469tex.target = GL_TEXTURE_2D;470tex.maxLod = 0.0f;471472// Create the surfaces.473glBindTexture(GL_TEXTURE_2D, tex.texture);474glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, fbo->width, fbo->height, 0, format, type, nullptr);475476tex.wrapS = GL_CLAMP_TO_EDGE;477tex.wrapT = GL_CLAMP_TO_EDGE;478tex.magFilter = linear ? GL_LINEAR : GL_NEAREST;479tex.minFilter = linear ? GL_LINEAR : GL_NEAREST;480481glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, tex.wrapS);482glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tex.wrapT);483glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, tex.magFilter);484glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, tex.minFilter);485if (!gl_extensions.IsGLES || gl_extensions.GLES3) {486glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);487}488};489490// Color texture is same everywhere491glGenFramebuffers(1, &fbo->handle);492initFBOTexture(fbo->color_texture, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, true);493494retry_depth:495if (!fbo->z_stencil_) {496INFO_LOG(Log::G3D, "Creating %d x %d FBO using no depth", fbo->width, fbo->height);497498fbo->z_stencil_buffer = 0;499fbo->stencil_buffer = 0;500fbo->z_buffer = 0;501502// Bind it all together503glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle);504glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture.texture, 0);505glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);506glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);507} else if (gl_extensions.IsGLES) {508if (gl_extensions.OES_packed_depth_stencil && (gl_extensions.OES_depth_texture || gl_extensions.GLES3)) {509INFO_LOG(Log::G3D, "Creating %d x %d FBO using DEPTH24_STENCIL8 texture", fbo->width, fbo->height);510fbo->z_stencil_buffer = 0;511fbo->stencil_buffer = 0;512fbo->z_buffer = 0;513514if (gl_extensions.GLES3) {515initFBOTexture(fbo->z_stencil_texture, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, false);516} else {517initFBOTexture(fbo->z_stencil_texture, GL_DEPTH_STENCIL, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, false);518}519520// Bind it all together521glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle);522glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture.texture, 0);523if (gl_extensions.GLES3) {524glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, fbo->z_stencil_texture.texture, 0);525} else {526glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, fbo->z_stencil_texture.texture, 0);527glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, fbo->z_stencil_texture.texture, 0);528}529} else if (gl_extensions.OES_packed_depth_stencil) {530INFO_LOG(Log::G3D, "Creating %d x %d FBO using DEPTH24_STENCIL8", fbo->width, fbo->height);531// Standard method532fbo->stencil_buffer = 0;533fbo->z_buffer = 0;534// 24-bit Z, 8-bit stencil combined535glGenRenderbuffers(1, &fbo->z_stencil_buffer);536glBindRenderbuffer(GL_RENDERBUFFER, fbo->z_stencil_buffer);537glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, fbo->width, fbo->height);538539// Bind it all together540glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle);541glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture.texture, 0);542glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer);543glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer);544} else {545INFO_LOG(Log::G3D, "Creating %d x %d FBO using separate stencil", fbo->width, fbo->height);546// TEGRA547fbo->z_stencil_buffer = 0;548// 16/24-bit Z, separate 8-bit stencil549glGenRenderbuffers(1, &fbo->z_buffer);550glBindRenderbuffer(GL_RENDERBUFFER, fbo->z_buffer);551// Don't forget to make sure fbo_standard_z_depth() matches.552glRenderbufferStorage(GL_RENDERBUFFER, gl_extensions.OES_depth24 ? GL_DEPTH_COMPONENT24 : GL_DEPTH_COMPONENT16, fbo->width, fbo->height);553554// 8-bit stencil buffer555glGenRenderbuffers(1, &fbo->stencil_buffer);556glBindRenderbuffer(GL_RENDERBUFFER, fbo->stencil_buffer);557glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, fbo->width, fbo->height);558559// Bind it all together560glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle);561glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture.texture, 0);562glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_buffer);563glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->stencil_buffer);564}565} else if (gl_extensions.VersionGEThan(3, 0)) {566INFO_LOG(Log::G3D, "Creating %d x %d FBO using DEPTH24_STENCIL8 texture", fbo->width, fbo->height);567fbo->z_stencil_buffer = 0;568fbo->stencil_buffer = 0;569fbo->z_buffer = 0;570571initFBOTexture(fbo->z_stencil_texture, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, false);572573// Bind it all together574glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle);575glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture.texture, 0);576glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, fbo->z_stencil_texture.texture, 0);577} else {578fbo->stencil_buffer = 0;579fbo->z_buffer = 0;580// 24-bit Z, 8-bit stencil581glGenRenderbuffers(1, &fbo->z_stencil_buffer);582glBindRenderbuffer(GL_RENDERBUFFER, fbo->z_stencil_buffer);583glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, fbo->width, fbo->height);584585// Bind it all together586glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle);587glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture.texture, 0);588glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer);589glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer);590}591592GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);593if (status != GL_FRAMEBUFFER_COMPLETE && !fbo->z_buffer) {594CHECK_GL_ERROR_IF_DEBUG();595// Uh oh, maybe we need a z/stencil. Platforms sometimes, right?596fbo->z_stencil_ = true;597goto retry_depth;598}599600switch (status) {601case GL_FRAMEBUFFER_COMPLETE:602// INFO_LOG(Log::G3D, "Framebuffer verified complete.");603break;604case GL_FRAMEBUFFER_UNSUPPORTED:605ERROR_LOG(Log::G3D, "GL_FRAMEBUFFER_UNSUPPORTED");606break;607case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:608ERROR_LOG(Log::G3D, "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT");609break;610default:611_assert_msg_(false, "Other framebuffer error: %d", status);612break;613}614615// Unbind state we don't need616glBindRenderbuffer(GL_RENDERBUFFER, 0);617glBindTexture(GL_TEXTURE_2D, 0);618CHECK_GL_ERROR_IF_DEBUG();619620currentDrawHandle_ = fbo->handle;621currentReadHandle_ = fbo->handle;622}623624void GLQueueRunner::RunSteps(const std::vector<GLRStep *> &steps, GLFrameData &frameData, bool skipGLCalls, bool keepSteps, bool useVR) {625if (skipGLCalls) {626if (keepSteps) {627return;628}629// Dry run630for (size_t i = 0; i < steps.size(); i++) {631const GLRStep &step = *steps[i];632switch (step.stepType) {633case GLRStepType::RENDER:634for (const auto &c : step.commands) {635switch (c.cmd) {636case GLRRenderCommand::TEXTURE_SUBIMAGE:637if (c.texture_subimage.data) {638if (c.texture_subimage.allocType == GLRAllocType::ALIGNED) {639FreeAlignedMemory(c.texture_subimage.data);640} else if (c.texture_subimage.allocType == GLRAllocType::NEW) {641delete[] c.texture_subimage.data;642}643}644break;645default:646break;647}648}649break;650default:651break;652}653delete steps[i];654}655return;656}657658size_t totalRenderCount = 0;659for (auto &step : steps) {660if (step->stepType == GLRStepType::RENDER) {661// Skip empty render steps.662if (step->commands.empty()) {663step->stepType = GLRStepType::RENDER_SKIP;664continue;665}666totalRenderCount++;667}668}669670CHECK_GL_ERROR_IF_DEBUG();671size_t renderCount = 0;672for (size_t i = 0; i < steps.size(); i++) {673GLRStep &step = *steps[i];674675#if !defined(USING_GLES2)676if (useDebugGroups_)677glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, (GLuint)i + 10000, -1, step.tag);678#endif679680switch (step.stepType) {681case GLRStepType::RENDER:682renderCount++;683if (IsVREnabled()) {684PreprocessStepVR(&step);685PerformRenderPass(step, renderCount == 1, renderCount == totalRenderCount, frameData.profile);686} else {687PerformRenderPass(step, renderCount == 1, renderCount == totalRenderCount, frameData.profile);688}689break;690case GLRStepType::COPY:691PerformCopy(step);692break;693case GLRStepType::BLIT:694PerformBlit(step);695break;696case GLRStepType::READBACK:697PerformReadback(step);698break;699case GLRStepType::READBACK_IMAGE:700PerformReadbackImage(step);701break;702case GLRStepType::RENDER_SKIP:703break;704default:705Crash();706break;707}708709#if !defined(USING_GLES2)710if (useDebugGroups_)711glPopDebugGroup();712#endif713if (frameData.profile.enabled) {714frameData.profile.passesString += StepToString(step);715}716if (!keepSteps) {717delete steps[i];718}719}720721CHECK_GL_ERROR_IF_DEBUG();722}723724void GLQueueRunner::PerformBlit(const GLRStep &step) {725CHECK_GL_ERROR_IF_DEBUG();726// Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing727// in ES 2.0 that actually separate them anyway of course, so doesn't matter.728fbo_bind_fb_target(false, step.blit.dst->handle);729fbo_bind_fb_target(true, step.blit.src->handle);730731int srcX1 = step.blit.srcRect.x;732int srcY1 = step.blit.srcRect.y;733int srcX2 = step.blit.srcRect.x + step.blit.srcRect.w;734int srcY2 = step.blit.srcRect.y + step.blit.srcRect.h;735int dstX1 = step.blit.dstRect.x;736int dstY1 = step.blit.dstRect.y;737int dstX2 = step.blit.dstRect.x + step.blit.dstRect.w;738int dstY2 = step.blit.dstRect.y + step.blit.dstRect.h;739740if (gl_extensions.GLES3 || gl_extensions.ARB_framebuffer_object) {741glBlitFramebuffer(srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2, step.blit.aspectMask, step.blit.filter ? GL_LINEAR : GL_NEAREST);742CHECK_GL_ERROR_IF_DEBUG();743#if defined(USING_GLES2) && defined(__ANDROID__) // We only support this extension on Android, it's not even available on PC.744} else if (gl_extensions.NV_framebuffer_blit) {745glBlitFramebufferNV(srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2, step.blit.aspectMask, step.blit.filter ? GL_LINEAR : GL_NEAREST);746CHECK_GL_ERROR_IF_DEBUG();747#endif // defined(USING_GLES2) && defined(__ANDROID__)748} else {749ERROR_LOG(Log::G3D, "GLQueueRunner: Tried to blit without the capability");750}751}752753static void EnableDisableVertexArrays(uint32_t prevAttr, uint32_t newAttr) {754int enable = (~prevAttr) & newAttr;755int disable = prevAttr & (~newAttr);756for (int i = 0; i < 7; i++) { // SEM_MAX757if (enable & (1 << i)) {758glEnableVertexAttribArray(i);759}760if (disable & (1 << i)) {761glDisableVertexAttribArray(i);762}763}764}765766void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last, GLQueueProfileContext &profile) {767CHECK_GL_ERROR_IF_DEBUG();768769PerformBindFramebufferAsRenderTarget(step);770771if (first) {772glDisable(GL_DEPTH_TEST);773glDisable(GL_STENCIL_TEST);774glDisable(GL_BLEND);775glDisable(GL_CULL_FACE);776glDisable(GL_DITHER);777glEnable(GL_SCISSOR_TEST);778#ifndef USING_GLES2779if (!gl_extensions.IsGLES) {780glDisable(GL_COLOR_LOGIC_OP);781}782#endif783if (gl_extensions.ARB_vertex_array_object) {784glBindVertexArray(globalVAO_);785}786}787788GLRProgram *curProgram = nullptr;789int activeSlot = -1;790791// State filtering tracking.792int attrMask = 0;793int colorMask = -1;794int depthMask = -1;795int depthFunc = -1;796GLuint curArrayBuffer = (GLuint)0;797GLuint curElemArrayBuffer = (GLuint)0;798bool depthEnabled = false;799bool stencilEnabled = false;800bool blendEnabled = false;801bool cullEnabled = false;802bool ditherEnabled = false;803bool depthClampEnabled = false;804#ifndef USING_GLES2805int logicOp = -1;806bool logicEnabled = false;807#endif808bool clipDistanceEnabled[8]{};809GLuint blendEqColor = (GLuint)-1;810GLuint blendEqAlpha = (GLuint)-1;811GLenum blendSrcColor = (GLenum)-1;812GLenum blendDstColor = (GLenum)-1;813GLenum blendSrcAlpha = (GLenum)-1;814GLenum blendDstAlpha = (GLenum)-1;815816GLuint stencilWriteMask = (GLuint)-1;817GLenum stencilFunc = (GLenum)-1;818GLuint stencilRef = (GLuint)-1;819GLuint stencilCompareMask = (GLuint)-1;820GLenum stencilSFail = (GLenum)-1;821GLenum stencilZFail = (GLenum)-1;822GLenum stencilPass = (GLenum)-1;823GLenum frontFace = (GLenum)-1;824GLenum cullFace = (GLenum)-1;825GLRTexture *curTex[MAX_GL_TEXTURE_SLOTS]{};826827GLRViewport viewport = {828-1000000000.0f,829-1000000000.0f,830-1000000000.0f,831-1000000000.0f,832-1000000000.0f,833-1000000000.0f,834};835836GLRect2D scissorRc = { -1, -1, -1, -1 };837838CHECK_GL_ERROR_IF_DEBUG();839auto &commands = step.commands;840for (const auto &c : commands) {841#ifdef _DEBUG842if (profile.enabled) {843if ((size_t)c.cmd < ARRAY_SIZE(profile.commandCounts)) {844profile.commandCounts[(size_t)c.cmd]++;845}846}847#endif848switch (c.cmd) {849case GLRRenderCommand::DEPTH:850if (c.depth.enabled) {851if (!depthEnabled) {852glEnable(GL_DEPTH_TEST);853depthEnabled = true;854}855if (c.depth.write != depthMask) {856glDepthMask(c.depth.write);857depthMask = c.depth.write;858}859if (c.depth.func != depthFunc) {860glDepthFunc(c.depth.func);861depthFunc = c.depth.func;862}863} else if (/* !c.depth.enabled && */ depthEnabled) {864glDisable(GL_DEPTH_TEST);865depthEnabled = false;866}867break;868case GLRRenderCommand::STENCIL:869if (c.stencil.enabled) {870if (!stencilEnabled) {871glEnable(GL_STENCIL_TEST);872stencilEnabled = true;873}874if (c.stencil.func != stencilFunc || c.stencil.ref != stencilRef || c.stencil.compareMask != stencilCompareMask) {875glStencilFunc(c.stencil.func, c.stencil.ref, c.stencil.compareMask);876stencilFunc = c.stencil.func;877stencilRef = c.stencil.ref;878stencilCompareMask = c.stencil.compareMask;879}880if (c.stencil.sFail != stencilSFail || c.stencil.zFail != stencilZFail || c.stencil.pass != stencilPass) {881glStencilOp(c.stencil.sFail, c.stencil.zFail, c.stencil.pass);882stencilSFail = c.stencil.sFail;883stencilZFail = c.stencil.zFail;884stencilPass = c.stencil.pass;885}886if (c.stencil.writeMask != stencilWriteMask) {887glStencilMask(c.stencil.writeMask);888stencilWriteMask = c.stencil.writeMask;889}890} else if (/* !c.stencilFunc.enabled && */stencilEnabled) {891glDisable(GL_STENCIL_TEST);892stencilEnabled = false;893}894CHECK_GL_ERROR_IF_DEBUG();895break;896case GLRRenderCommand::BLEND:897if (c.blend.enabled) {898if (!blendEnabled) {899glEnable(GL_BLEND);900blendEnabled = true;901}902if (blendEqColor != c.blend.funcColor || blendEqAlpha != c.blend.funcAlpha) {903glBlendEquationSeparate(c.blend.funcColor, c.blend.funcAlpha);904blendEqColor = c.blend.funcColor;905blendEqAlpha = c.blend.funcAlpha;906}907if (blendSrcColor != c.blend.srcColor || blendDstColor != c.blend.dstColor || blendSrcAlpha != c.blend.srcAlpha || blendDstAlpha != c.blend.dstAlpha) {908glBlendFuncSeparate(c.blend.srcColor, c.blend.dstColor, c.blend.srcAlpha, c.blend.dstAlpha);909blendSrcColor = c.blend.srcColor;910blendDstColor = c.blend.dstColor;911blendSrcAlpha = c.blend.srcAlpha;912blendDstAlpha = c.blend.dstAlpha;913}914} else if (/* !c.blend.enabled && */ blendEnabled) {915glDisable(GL_BLEND);916blendEnabled = false;917}918if (c.blend.mask != colorMask) {919glColorMask(c.blend.mask & 1, (c.blend.mask >> 1) & 1, (c.blend.mask >> 2) & 1, (c.blend.mask >> 3) & 1);920colorMask = c.blend.mask;921}922CHECK_GL_ERROR_IF_DEBUG();923break;924case GLRRenderCommand::LOGICOP:925#ifndef USING_GLES2926if (c.logic.enabled) {927if (!logicEnabled) {928glEnable(GL_COLOR_LOGIC_OP);929logicEnabled = true;930}931if (logicOp != c.logic.logicOp) {932glLogicOp(c.logic.logicOp);933}934} else if (/* !c.logic.enabled && */ logicEnabled) {935glDisable(GL_COLOR_LOGIC_OP);936logicEnabled = false;937}938#endif939CHECK_GL_ERROR_IF_DEBUG();940break;941case GLRRenderCommand::CLEAR:942// Scissor test is on, and should be on after leaving this case. If we disable it,943// we re-enable it at the end.944if (c.clear.scissorW == 0) {945glDisable(GL_SCISSOR_TEST);946} else {947glScissor(c.clear.scissorX, c.clear.scissorY, c.clear.scissorW, c.clear.scissorH);948}949if (c.clear.colorMask != colorMask) {950glColorMask(c.clear.colorMask & 1, (c.clear.colorMask >> 1) & 1, (c.clear.colorMask >> 2) & 1, (c.clear.colorMask >> 3) & 1);951}952if (c.clear.clearMask & GL_COLOR_BUFFER_BIT) {953float color[4];954Uint8x4ToFloat4(color, c.clear.clearColor);955glClearColor(color[0], color[1], color[2], color[3]);956}957if (c.clear.clearMask & GL_DEPTH_BUFFER_BIT) {958#if defined(USING_GLES2)959glClearDepthf(c.clear.clearZ);960#else961if (gl_extensions.IsGLES) {962glClearDepthf(c.clear.clearZ);963} else {964glClearDepth(c.clear.clearZ);965}966#endif967}968if (c.clear.clearMask & GL_STENCIL_BUFFER_BIT) {969glClearStencil(c.clear.clearStencil);970}971glClear(c.clear.clearMask);972// Restore the color mask if it was different.973if (c.clear.colorMask != colorMask) {974glColorMask(colorMask & 1, (colorMask >> 1) & 1, (colorMask >> 2) & 1, (colorMask >> 3) & 1);975}976if (c.clear.scissorW == 0) {977glEnable(GL_SCISSOR_TEST);978}979CHECK_GL_ERROR_IF_DEBUG();980break;981case GLRRenderCommand::BLENDCOLOR:982glBlendColor(c.blendColor.color[0], c.blendColor.color[1], c.blendColor.color[2], c.blendColor.color[3]);983break;984case GLRRenderCommand::VIEWPORT:985{986float y = c.viewport.vp.y;987if (!curFB_)988y = curFBHeight_ - y - c.viewport.vp.h;989990// TODO: Support FP viewports through glViewportArrays991if (viewport.x != c.viewport.vp.x || viewport.y != y || viewport.w != c.viewport.vp.w || viewport.h != c.viewport.vp.h) {992glViewport((GLint)c.viewport.vp.x, (GLint)y, (GLsizei)c.viewport.vp.w, (GLsizei)c.viewport.vp.h);993viewport.x = c.viewport.vp.x;994viewport.y = y;995viewport.w = c.viewport.vp.w;996viewport.h = c.viewport.vp.h;997}998999if (viewport.minZ != c.viewport.vp.minZ || viewport.maxZ != c.viewport.vp.maxZ) {1000viewport.minZ = c.viewport.vp.minZ;1001viewport.maxZ = c.viewport.vp.maxZ;1002#if !defined(USING_GLES2)1003if (gl_extensions.IsGLES) {1004glDepthRangef(c.viewport.vp.minZ, c.viewport.vp.maxZ);1005} else {1006glDepthRange(c.viewport.vp.minZ, c.viewport.vp.maxZ);1007}1008#else1009glDepthRangef(c.viewport.vp.minZ, c.viewport.vp.maxZ);1010#endif1011}1012CHECK_GL_ERROR_IF_DEBUG();1013break;1014}1015case GLRRenderCommand::SCISSOR:1016{1017int y = c.scissor.rc.y;1018if (!curFB_)1019y = curFBHeight_ - y - c.scissor.rc.h;1020if (scissorRc.x != c.scissor.rc.x || scissorRc.y != y || scissorRc.w != c.scissor.rc.w || scissorRc.h != c.scissor.rc.h) {1021glScissor(c.scissor.rc.x, y, c.scissor.rc.w, c.scissor.rc.h);1022scissorRc.x = c.scissor.rc.x;1023scissorRc.y = y;1024scissorRc.w = c.scissor.rc.w;1025scissorRc.h = c.scissor.rc.h;1026}1027CHECK_GL_ERROR_IF_DEBUG();1028break;1029}1030case GLRRenderCommand::UNIFORM4F:1031{1032_dbg_assert_(curProgram);1033int loc = c.uniform4.loc ? *c.uniform4.loc : -1;1034if (c.uniform4.name) {1035loc = curProgram->GetUniformLoc(c.uniform4.name);1036}1037if (loc >= 0) {1038_dbg_assert_(c.uniform4.count >=1 && c.uniform4.count <=4);1039switch (c.uniform4.count) {1040case 1: glUniform1f(loc, c.uniform4.v[0]); break;1041case 2: glUniform2fv(loc, 1, c.uniform4.v); break;1042case 3: glUniform3fv(loc, 1, c.uniform4.v); break;1043case 4: glUniform4fv(loc, 1, c.uniform4.v); break;1044}1045}1046CHECK_GL_ERROR_IF_DEBUG();1047break;1048}1049case GLRRenderCommand::UNIFORM4UI:1050{1051_dbg_assert_(curProgram);1052int loc = c.uniform4.loc ? *c.uniform4.loc : -1;1053if (c.uniform4.name) {1054loc = curProgram->GetUniformLoc(c.uniform4.name);1055}1056if (loc >= 0) {1057_dbg_assert_(c.uniform4.count >=1 && c.uniform4.count <=4);1058switch (c.uniform4.count) {1059case 1: glUniform1uiv(loc, 1, (GLuint *)c.uniform4.v); break;1060case 2: glUniform2uiv(loc, 1, (GLuint *)c.uniform4.v); break;1061case 3: glUniform3uiv(loc, 1, (GLuint *)c.uniform4.v); break;1062case 4: glUniform4uiv(loc, 1, (GLuint *)c.uniform4.v); break;1063}1064}1065CHECK_GL_ERROR_IF_DEBUG();1066break;1067}1068case GLRRenderCommand::UNIFORM4I:1069{1070_dbg_assert_(curProgram);1071int loc = c.uniform4.loc ? *c.uniform4.loc : -1;1072if (c.uniform4.name) {1073loc = curProgram->GetUniformLoc(c.uniform4.name);1074}1075if (loc >= 0) {1076_dbg_assert_(c.uniform4.count >=1 && c.uniform4.count <=4);1077switch (c.uniform4.count) {1078case 1: glUniform1iv(loc, 1, (GLint *)c.uniform4.v); break;1079case 2: glUniform2iv(loc, 1, (GLint *)c.uniform4.v); break;1080case 3: glUniform3iv(loc, 1, (GLint *)c.uniform4.v); break;1081case 4: glUniform4iv(loc, 1, (GLint *)c.uniform4.v); break;1082}1083}1084CHECK_GL_ERROR_IF_DEBUG();1085break;1086}1087case GLRRenderCommand::UNIFORMSTEREOMATRIX:1088{1089_dbg_assert_(curProgram);1090int loc = c.uniformStereoMatrix4.loc ? *c.uniformStereoMatrix4.loc : -1;1091if (c.uniformStereoMatrix4.name) {1092loc = curProgram->GetUniformLoc(c.uniformStereoMatrix4.name);1093}1094if (loc >= 0) {1095if (GetVRFBOIndex() == 0) {1096glUniformMatrix4fv(loc, 1, false, c.uniformStereoMatrix4.mData);1097} else {1098glUniformMatrix4fv(loc, 1, false, c.uniformStereoMatrix4.mData + 16);1099}1100}1101if (GetVRFBOIndex() == 1 || GetVRPassesCount() == 1) {1102// Only delete the data if we're rendering the only or the second eye.1103// If we delete during the first eye, we get a use-after-free or double delete.1104delete[] c.uniformStereoMatrix4.mData;1105}1106CHECK_GL_ERROR_IF_DEBUG();1107break;1108}1109case GLRRenderCommand::UNIFORMMATRIX:1110{1111_dbg_assert_(curProgram);1112int loc = c.uniformMatrix4.loc ? *c.uniformMatrix4.loc : -1;1113if (c.uniformMatrix4.name) {1114loc = curProgram->GetUniformLoc(c.uniformMatrix4.name);1115}1116if (loc >= 0) {1117glUniformMatrix4fv(loc, 1, false, c.uniformMatrix4.m);1118}1119CHECK_GL_ERROR_IF_DEBUG();1120break;1121}1122case GLRRenderCommand::BINDTEXTURE:1123{1124GLint slot = c.texture.slot;1125if (slot != activeSlot) {1126glActiveTexture(GL_TEXTURE0 + slot);1127activeSlot = slot;1128}1129if (c.texture.texture) {1130if (curTex[slot] != c.texture.texture) {1131glBindTexture(c.texture.texture->target, c.texture.texture->texture);1132curTex[slot] = c.texture.texture;1133}1134} else {1135glBindTexture(GL_TEXTURE_2D, 0); // Which target? Well we only use this one anyway...1136curTex[slot] = nullptr;1137}1138CHECK_GL_ERROR_IF_DEBUG();1139break;1140}1141case GLRRenderCommand::BIND_FB_TEXTURE:1142{1143GLint slot = c.bind_fb_texture.slot;1144if (slot != activeSlot) {1145glActiveTexture(GL_TEXTURE0 + slot);1146activeSlot = slot;1147}1148if (c.bind_fb_texture.aspect == GL_COLOR_BUFFER_BIT) {1149if (curTex[slot] != &c.bind_fb_texture.framebuffer->color_texture) {1150glBindTexture(GL_TEXTURE_2D, c.bind_fb_texture.framebuffer->color_texture.texture);1151curTex[slot] = &c.bind_fb_texture.framebuffer->color_texture;1152}1153} else if (c.bind_fb_texture.aspect == GL_DEPTH_BUFFER_BIT) {1154if (curTex[slot] != &c.bind_fb_texture.framebuffer->z_stencil_texture) {1155glBindTexture(GL_TEXTURE_2D, c.bind_fb_texture.framebuffer->z_stencil_texture.texture);1156curTex[slot] = &c.bind_fb_texture.framebuffer->z_stencil_texture;1157}1158// This should be uncommon, so always set the mode.1159glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT);1160} else if (c.bind_fb_texture.aspect == GL_STENCIL_BUFFER_BIT) {1161if (curTex[slot] != &c.bind_fb_texture.framebuffer->z_stencil_texture) {1162glBindTexture(GL_TEXTURE_2D, c.bind_fb_texture.framebuffer->z_stencil_texture.texture);1163curTex[slot] = &c.bind_fb_texture.framebuffer->z_stencil_texture;1164}1165// This should be uncommon, so always set the mode.1166glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX);1167} else {1168curTex[slot] = nullptr;1169}1170CHECK_GL_ERROR_IF_DEBUG();1171break;1172}1173case GLRRenderCommand::BINDPROGRAM:1174{1175if (curProgram != c.program.program) {1176glUseProgram(c.program.program->program);1177curProgram = c.program.program;11781179for (size_t i = 0; i < ARRAY_SIZE(clipDistanceEnabled); ++i) {1180if (c.program.program->use_clip_distance[i] == clipDistanceEnabled[i])1181continue;11821183if (c.program.program->use_clip_distance[i])1184glEnable(GL_CLIP_DISTANCE0 + (GLenum)i);1185else1186glDisable(GL_CLIP_DISTANCE0 + (GLenum)i);1187clipDistanceEnabled[i] = c.program.program->use_clip_distance[i];1188}1189}1190CHECK_GL_ERROR_IF_DEBUG();1191break;1192}1193case GLRRenderCommand::DRAW:1194{1195GLRInputLayout *layout = c.draw.inputLayout;1196GLuint buf = c.draw.vertexBuffer->buffer_;1197_dbg_assert_(!c.draw.vertexBuffer->Mapped());1198if (buf != curArrayBuffer) {1199glBindBuffer(GL_ARRAY_BUFFER, buf);1200curArrayBuffer = buf;1201}1202if (attrMask != layout->semanticsMask_) {1203EnableDisableVertexArrays(attrMask, layout->semanticsMask_);1204attrMask = layout->semanticsMask_;1205}1206for (size_t i = 0; i < layout->entries.size(); i++) {1207auto &entry = layout->entries[i];1208glVertexAttribPointer(entry.location, entry.count, entry.type, entry.normalized, layout->stride, (const void *)(c.draw.vertexOffset + entry.offset));1209}1210if (c.draw.indexBuffer) {1211GLuint buf = c.draw.indexBuffer->buffer_;1212_dbg_assert_(!c.draw.indexBuffer->Mapped());1213if (buf != curElemArrayBuffer) {1214glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf);1215curElemArrayBuffer = buf;1216}1217if (c.draw.instances == 1) {1218glDrawElements(c.draw.mode, c.draw.count, c.draw.indexType, (void *)(intptr_t)c.draw.indexOffset);1219} else {1220glDrawElementsInstanced(c.draw.mode, c.draw.count, c.draw.indexType, (void *)(intptr_t)c.draw.indexOffset, c.draw.instances);1221}1222} else {1223glDrawArrays(c.draw.mode, c.draw.first, c.draw.count);1224}1225CHECK_GL_ERROR_IF_DEBUG();1226break;1227}1228case GLRRenderCommand::GENMIPS:1229// TODO: Should we include the texture handle in the command?1230// Also, should this not be an init command?1231glGenerateMipmap(GL_TEXTURE_2D);1232CHECK_GL_ERROR_IF_DEBUG();1233break;1234case GLRRenderCommand::TEXTURESAMPLER:1235{1236CHECK_GL_ERROR_IF_DEBUG();1237GLint slot = c.textureSampler.slot;1238if (slot != activeSlot) {1239glActiveTexture(GL_TEXTURE0 + slot);1240activeSlot = slot;1241}1242GLRTexture *tex = curTex[slot];1243if (!tex) {1244break;1245}1246CHECK_GL_ERROR_IF_DEBUG();1247if (tex->canWrap) {1248if (tex->wrapS != c.textureSampler.wrapS) {1249glTexParameteri(tex->target, GL_TEXTURE_WRAP_S, c.textureSampler.wrapS);1250tex->wrapS = c.textureSampler.wrapS;1251}1252if (tex->wrapT != c.textureSampler.wrapT) {1253glTexParameteri(tex->target, GL_TEXTURE_WRAP_T, c.textureSampler.wrapT);1254tex->wrapT = c.textureSampler.wrapT;1255}1256}1257CHECK_GL_ERROR_IF_DEBUG();1258if (tex->magFilter != c.textureSampler.magFilter) {1259glTexParameteri(tex->target, GL_TEXTURE_MAG_FILTER, c.textureSampler.magFilter);1260tex->magFilter = c.textureSampler.magFilter;1261}1262CHECK_GL_ERROR_IF_DEBUG();1263if (tex->minFilter != c.textureSampler.minFilter) {1264glTexParameteri(tex->target, GL_TEXTURE_MIN_FILTER, c.textureSampler.minFilter);1265tex->minFilter = c.textureSampler.minFilter;1266}1267CHECK_GL_ERROR_IF_DEBUG();1268if (tex->anisotropy != c.textureSampler.anisotropy) {1269if (c.textureSampler.anisotropy != 0.0f) {1270glTexParameterf(tex->target, GL_TEXTURE_MAX_ANISOTROPY_EXT, c.textureSampler.anisotropy);1271}1272tex->anisotropy = c.textureSampler.anisotropy;1273}1274CHECK_GL_ERROR_IF_DEBUG();1275break;1276}1277case GLRRenderCommand::TEXTURELOD:1278{1279GLint slot = c.textureLod.slot;1280if (slot != activeSlot) {1281glActiveTexture(GL_TEXTURE0 + slot);1282activeSlot = slot;1283}1284GLRTexture *tex = curTex[slot];1285if (!tex) {1286break;1287}1288#ifndef USING_GLES21289if (tex->lodBias != c.textureLod.lodBias && !gl_extensions.IsGLES) {1290glTexParameterf(tex->target, GL_TEXTURE_LOD_BIAS, c.textureLod.lodBias);1291tex->lodBias = c.textureLod.lodBias;1292}1293#endif1294if (tex->minLod != c.textureLod.minLod) {1295glTexParameterf(tex->target, GL_TEXTURE_MIN_LOD, c.textureLod.minLod);1296tex->minLod = c.textureLod.minLod;1297}1298if (tex->maxLod != c.textureLod.maxLod) {1299glTexParameterf(tex->target, GL_TEXTURE_MAX_LOD, c.textureLod.maxLod);1300tex->maxLod = c.textureLod.maxLod;1301}1302break;1303}1304case GLRRenderCommand::TEXTURE_SUBIMAGE:1305{1306GLint slot = c.texture_subimage.slot;1307if (slot != activeSlot) {1308glActiveTexture(GL_TEXTURE0 + slot);1309activeSlot = slot;1310}1311// TODO: Need bind?1312GLRTexture *tex = c.texture_subimage.texture;1313if (!c.texture_subimage.data)1314Crash();1315_assert_(tex->target == GL_TEXTURE_2D);1316// For things to show in RenderDoc, need to split into glTexImage2D(..., nullptr) and glTexSubImage.1317GLuint internalFormat, format, type;1318int alignment;1319Thin3DFormatToGLFormatAndType(c.texture_subimage.format, internalFormat, format, type, alignment);1320glTexSubImage2D(tex->target, c.texture_subimage.level, c.texture_subimage.x, c.texture_subimage.y, c.texture_subimage.width, c.texture_subimage.height, format, type, c.texture_subimage.data);1321if (c.texture_subimage.allocType == GLRAllocType::ALIGNED) {1322FreeAlignedMemory(c.texture_subimage.data);1323} else if (c.texture_subimage.allocType == GLRAllocType::NEW) {1324delete[] c.texture_subimage.data;1325}1326CHECK_GL_ERROR_IF_DEBUG();1327break;1328}1329case GLRRenderCommand::RASTER:1330if (c.raster.cullEnable) {1331if (!cullEnabled) {1332glEnable(GL_CULL_FACE);1333cullEnabled = true;1334}1335if (frontFace != c.raster.frontFace) {1336glFrontFace(c.raster.frontFace);1337frontFace = c.raster.frontFace;1338}1339if (cullFace != c.raster.cullFace) {1340glCullFace(c.raster.cullFace);1341cullFace = c.raster.cullFace;1342}1343} else if (/* !c.raster.cullEnable && */ cullEnabled) {1344glDisable(GL_CULL_FACE);1345cullEnabled = false;1346}1347if (c.raster.ditherEnable) {1348if (!ditherEnabled) {1349glEnable(GL_DITHER);1350ditherEnabled = true;1351}1352} else if (/* !c.raster.ditherEnable && */ ditherEnabled) {1353glDisable(GL_DITHER);1354ditherEnabled = false;1355}1356#ifndef USING_GLES21357if (c.raster.depthClampEnable) {1358if (!depthClampEnabled) {1359glEnable(GL_DEPTH_CLAMP);1360depthClampEnabled = true;1361}1362} else if (/* !c.raster.depthClampEnable && */ depthClampEnabled) {1363glDisable(GL_DEPTH_CLAMP);1364depthClampEnabled = false;1365}1366#endif1367CHECK_GL_ERROR_IF_DEBUG();1368break;1369default:1370_assert_msg_(false, "Bad GLRRenderCommand: %d", (int)c.cmd);1371break;1372}1373}13741375for (int i = 0; i < 7; i++) {1376if (attrMask & (1 << i)) {1377glDisableVertexAttribArray(i);1378}1379}13801381if (activeSlot != 0) {1382glActiveTexture(GL_TEXTURE0);1383activeSlot = 0; // doesn't matter, just nice.1384}1385CHECK_GL_ERROR_IF_DEBUG();13861387// Wipe out the current state.1388if (curArrayBuffer != 0)1389glBindBuffer(GL_ARRAY_BUFFER, 0);1390if (curElemArrayBuffer != 0)1391glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);1392if (last && gl_extensions.ARB_vertex_array_object) {1393glBindVertexArray(0);1394}1395if (last)1396glDisable(GL_SCISSOR_TEST);1397if (depthEnabled)1398glDisable(GL_DEPTH_TEST);1399if (stencilEnabled)1400glDisable(GL_STENCIL_TEST);1401if (blendEnabled)1402glDisable(GL_BLEND);1403if (cullEnabled)1404glDisable(GL_CULL_FACE);1405#ifndef USING_GLES21406if (depthClampEnabled)1407glDisable(GL_DEPTH_CLAMP);1408if (!gl_extensions.IsGLES && logicEnabled) {1409glDisable(GL_COLOR_LOGIC_OP);1410}1411#endif1412for (size_t i = 0; i < ARRAY_SIZE(clipDistanceEnabled); ++i) {1413if (clipDistanceEnabled[i])1414glDisable(GL_CLIP_DISTANCE0 + (GLenum)i);1415}1416if ((colorMask & 15) != 15)1417glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);1418CHECK_GL_ERROR_IF_DEBUG();1419}14201421void GLQueueRunner::PerformCopy(const GLRStep &step) {1422CHECK_GL_ERROR_IF_DEBUG();1423GLuint srcTex = 0;1424GLuint dstTex = 0;1425GLuint target = GL_TEXTURE_2D;14261427const GLRect2D &srcRect = step.copy.srcRect;1428const GLOffset2D &dstPos = step.copy.dstPos;14291430GLRFramebuffer *src = step.copy.src;1431GLRFramebuffer *dst = step.copy.dst;14321433int srcLevel = 0;1434int dstLevel = 0;1435int srcZ = 0;1436int dstZ = 0;1437int depth = 1;14381439switch (step.copy.aspectMask) {1440case GL_COLOR_BUFFER_BIT:1441srcTex = src->color_texture.texture;1442dstTex = dst->color_texture.texture;1443break;1444case GL_DEPTH_BUFFER_BIT:1445// TODO: Support depth copies.1446_assert_msg_(false, "Depth copies not yet supported - soon");1447target = GL_RENDERBUFFER;1448/*1449srcTex = src->depth.texture;1450dstTex = src->depth.texture;1451*/1452break;1453}14541455_dbg_assert_(srcTex);1456_dbg_assert_(dstTex);14571458_assert_msg_(caps_.framebufferCopySupported, "Image copy extension expected");14591460#if defined(USING_GLES2)1461#if !PPSSPP_PLATFORM(IOS)1462glCopyImageSubDataOES(1463srcTex, target, srcLevel, srcRect.x, srcRect.y, srcZ,1464dstTex, target, dstLevel, dstPos.x, dstPos.y, dstZ,1465srcRect.w, srcRect.h, depth);1466#endif1467#else1468if (gl_extensions.ARB_copy_image) {1469glCopyImageSubData(1470srcTex, target, srcLevel, srcRect.x, srcRect.y, srcZ,1471dstTex, target, dstLevel, dstPos.x, dstPos.y, dstZ,1472srcRect.w, srcRect.h, depth);1473} else if (gl_extensions.NV_copy_image) {1474// Older, pre GL 4.x NVIDIA cards.1475glCopyImageSubDataNV(1476srcTex, target, srcLevel, srcRect.x, srcRect.y, srcZ,1477dstTex, target, dstLevel, dstPos.x, dstPos.y, dstZ,1478srcRect.w, srcRect.h, depth);1479}1480#endif1481CHECK_GL_ERROR_IF_DEBUG();1482}14831484void GLQueueRunner::PerformReadback(const GLRStep &pass) {1485using namespace Draw;1486CHECK_GL_ERROR_IF_DEBUG();14871488GLRFramebuffer *fb = pass.readback.src;14891490fbo_bind_fb_target(true, fb ? fb->handle : 0);14911492// Reads from the "bound for read" framebuffer. Note that if there's no fb, it's not valid to call this.1493if (fb && (gl_extensions.GLES3 || !gl_extensions.IsGLES))1494glReadBuffer(GL_COLOR_ATTACHMENT0);14951496CHECK_GL_ERROR_IF_DEBUG();14971498// Always read back in 8888 format for the color aspect.1499GLuint format = GL_RGBA;1500GLuint type = GL_UNSIGNED_BYTE;1501int srcAlignment = 4;15021503#ifndef USING_GLES21504if (pass.readback.aspectMask & GL_DEPTH_BUFFER_BIT) {1505format = GL_DEPTH_COMPONENT;1506type = GL_FLOAT;1507srcAlignment = 4;1508} else if (pass.readback.aspectMask & GL_STENCIL_BUFFER_BIT) {1509format = GL_STENCIL_INDEX;1510type = GL_UNSIGNED_BYTE;1511srcAlignment = 1;1512}1513#endif15141515readbackAspectMask_ = pass.readback.aspectMask;15161517int pixelStride = pass.readback.srcRect.w;1518// Apply the correct alignment.1519glPixelStorei(GL_PACK_ALIGNMENT, srcAlignment);1520if (!gl_extensions.IsGLES || gl_extensions.GLES3) {1521// Some drivers seem to require we specify this. See #8254.1522glPixelStorei(GL_PACK_ROW_LENGTH, pixelStride);1523}15241525GLRect2D rect = pass.readback.srcRect;15261527int readbackSize = srcAlignment * rect.w * rect.h;1528if (readbackSize > readbackBufferSize_) {1529delete[] readbackBuffer_;1530readbackBuffer_ = new uint8_t[readbackSize];1531readbackBufferSize_ = readbackSize;1532}15331534glReadPixels(rect.x, rect.y, rect.w, rect.h, format, type, readbackBuffer_);1535#ifdef DEBUG_READ_PIXELS1536LogReadPixelsError(glGetError());1537#endif1538if (!gl_extensions.IsGLES || gl_extensions.GLES3) {1539glPixelStorei(GL_PACK_ROW_LENGTH, 0);1540}1541CHECK_GL_ERROR_IF_DEBUG();1542}15431544void GLQueueRunner::PerformReadbackImage(const GLRStep &pass) {1545#ifndef USING_GLES21546GLRTexture *tex = pass.readback_image.texture;1547GLRect2D rect = pass.readback_image.srcRect;15481549if (gl_extensions.VersionGEThan(4, 5)) {1550int size = 4 * rect.w * rect.h;1551if (size > readbackBufferSize_) {1552delete[] readbackBuffer_;1553readbackBuffer_ = new uint8_t[size];1554readbackBufferSize_ = size;1555}15561557glPixelStorei(GL_PACK_ALIGNMENT, 4);1558glGetTextureSubImage(tex->texture, pass.readback_image.mipLevel, rect.x, rect.y, 0, rect.w, rect.h, 1, GL_RGBA, GL_UNSIGNED_BYTE, readbackBufferSize_, readbackBuffer_);1559} else {1560glBindTexture(GL_TEXTURE_2D, tex->texture);15611562CHECK_GL_ERROR_IF_DEBUG();15631564GLint w, h;1565// This is only used for debugging (currently), and GL doesn't support a subrectangle.1566glGetTexLevelParameteriv(GL_TEXTURE_2D, pass.readback_image.mipLevel, GL_TEXTURE_WIDTH, &w);1567glGetTexLevelParameteriv(GL_TEXTURE_2D, pass.readback_image.mipLevel, GL_TEXTURE_HEIGHT, &h);15681569int size = 4 * std::max((int)w, rect.x + rect.w) * std::max((int)h, rect.y + rect.h);1570if (size > readbackBufferSize_) {1571delete[] readbackBuffer_;1572readbackBuffer_ = new uint8_t[size];1573readbackBufferSize_ = size;1574}15751576glPixelStorei(GL_PACK_ALIGNMENT, 4);1577glPixelStorei(GL_PACK_ROW_LENGTH, rect.x + rect.w);1578glGetTexImage(GL_TEXTURE_2D, pass.readback_image.mipLevel, GL_RGBA, GL_UNSIGNED_BYTE, readbackBuffer_);1579glPixelStorei(GL_PACK_ROW_LENGTH, 0);15801581if (rect.x != 0 || rect.y != 0) {1582int dstStride = 4 * rect.w;1583int srcStride = 4 * (rect.x + rect.w);1584int xoff = 4 * rect.x;1585int yoff = rect.y * srcStride;1586for (int y = 0; y < rect.h; ++y) {1587memmove(readbackBuffer_ + h * dstStride, readbackBuffer_ + yoff + h * srcStride + xoff, dstStride);1588}1589}1590}1591#endif15921593CHECK_GL_ERROR_IF_DEBUG();1594}15951596void GLQueueRunner::PerformBindFramebufferAsRenderTarget(const GLRStep &pass) {1597if (pass.render.framebuffer) {1598curFBWidth_ = pass.render.framebuffer->width;1599curFBHeight_ = pass.render.framebuffer->height;1600} else {1601curFBWidth_ = targetWidth_;1602curFBHeight_ = targetHeight_;1603}16041605curFB_ = pass.render.framebuffer;1606if (curFB_) {1607// Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing1608// in ES 2.0 that actually separate them anyway of course, so doesn't matter.1609fbo_bind_fb_target(false, curFB_->handle);1610} else {1611fbo_unbind();1612if (IsVREnabled()) {1613BindVRFramebuffer();1614}1615// Backbuffer is now bound.1616}1617CHECK_GL_ERROR_IF_DEBUG();1618}16191620void GLQueueRunner::CopyFromReadbackBuffer(GLRFramebuffer *framebuffer, int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels) {1621// TODO: Maybe move data format conversion here, and always read back 8888. Drivers1622// don't usually provide very optimized conversion implementations, though some do.1623// Just need to be careful about dithering, which may break Danganronpa.1624int bpp = (int)Draw::DataFormatSizeInBytes(destFormat);1625if (!readbackBuffer_ || bpp <= 0 || !pixels) {1626// Something went wrong during the read and no readback buffer was allocated, probably.1627return;1628}16291630// Always read back in 8888 format for the color aspect.1631GLuint internalFormat = GL_RGBA;1632#ifndef USING_GLES21633if (readbackAspectMask_ & GL_DEPTH_BUFFER_BIT) {1634internalFormat = GL_DEPTH_COMPONENT;1635} else if (readbackAspectMask_ & GL_STENCIL_BUFFER_BIT) {1636internalFormat = GL_STENCIL_INDEX;1637}1638#endif16391640bool convert = internalFormat == GL_RGBA && destFormat != Draw::DataFormat::R8G8B8A8_UNORM;1641if (convert) {1642// srcStride is width because we read back "packed" (with no gaps) from GL.1643ConvertFromRGBA8888(pixels, readbackBuffer_, pixelStride, width, width, height, destFormat);1644} else {1645for (int y = 0; y < height; y++) {1646memcpy(pixels + y * pixelStride * bpp, readbackBuffer_ + y * width * bpp, width * bpp);1647}1648}1649}16501651// On PC, we always use GL_DEPTH24_STENCIL8.1652// On Android, we try to use what's available.16531654#ifndef USING_GLES21655void GLQueueRunner::fbo_ext_create(const GLRInitStep &step) {1656GLRFramebuffer *fbo = step.create_framebuffer.framebuffer;16571658CHECK_GL_ERROR_IF_DEBUG();16591660// Color texture is same everywhere1661glGenFramebuffersEXT(1, &fbo->handle);1662glGenTextures(1, &fbo->color_texture.texture);16631664// Create the surfaces.1665glBindTexture(GL_TEXTURE_2D, fbo->color_texture.texture);1666glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);16671668fbo->color_texture.target = GL_TEXTURE_2D;1669fbo->color_texture.wrapS = GL_CLAMP_TO_EDGE;1670fbo->color_texture.wrapT = GL_CLAMP_TO_EDGE;1671fbo->color_texture.magFilter = GL_LINEAR;1672fbo->color_texture.minFilter = GL_LINEAR;1673fbo->color_texture.maxLod = 0.0f;1674glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, fbo->color_texture.wrapS);1675glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, fbo->color_texture.wrapT);1676glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, fbo->color_texture.magFilter);1677glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, fbo->color_texture.minFilter);16781679fbo->stencil_buffer = 0;1680fbo->z_buffer = 0;1681// 24-bit Z, 8-bit stencil1682glGenRenderbuffersEXT(1, &fbo->z_stencil_buffer);1683glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo->z_stencil_buffer);1684glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_EXT, fbo->width, fbo->height);1685// glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8, width, height);16861687// Bind it all together1688glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->handle);1689glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, fbo->color_texture.texture, 0);1690glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo->z_stencil_buffer);1691glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo->z_stencil_buffer);16921693GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);1694switch (status) {1695case GL_FRAMEBUFFER_COMPLETE_EXT:1696// INFO_LOG(Log::G3D, "Framebuffer verified complete.");1697break;1698case GL_FRAMEBUFFER_UNSUPPORTED_EXT:1699ERROR_LOG(Log::G3D, "GL_FRAMEBUFFER_UNSUPPORTED");1700break;1701case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:1702ERROR_LOG(Log::G3D, "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT ");1703break;1704default:1705_assert_msg_(false, "Other framebuffer error: %d", status);1706break;1707}1708// Unbind state we don't need1709glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);1710glBindTexture(GL_TEXTURE_2D, 0);17111712CHECK_GL_ERROR_IF_DEBUG();17131714currentDrawHandle_ = fbo->handle;1715currentReadHandle_ = fbo->handle;1716}1717#endif17181719GLenum GLQueueRunner::fbo_get_fb_target(bool read, GLuint **cached) {1720bool supportsBlit = gl_extensions.ARB_framebuffer_object;1721if (gl_extensions.IsGLES) {1722supportsBlit = (gl_extensions.GLES3 || gl_extensions.NV_framebuffer_blit);1723}17241725// Note: GL_FRAMEBUFFER_EXT and GL_FRAMEBUFFER have the same value, same with _NV.1726if (supportsBlit) {1727if (read) {1728*cached = ¤tReadHandle_;1729return GL_READ_FRAMEBUFFER;1730} else {1731*cached = ¤tDrawHandle_;1732return GL_DRAW_FRAMEBUFFER;1733}1734} else {1735*cached = ¤tDrawHandle_;1736return GL_FRAMEBUFFER;1737}1738}17391740void GLQueueRunner::fbo_bind_fb_target(bool read, GLuint name) {1741CHECK_GL_ERROR_IF_DEBUG();1742GLuint *cached;1743GLenum target = fbo_get_fb_target(read, &cached);1744if (*cached != name) {1745if (gl_extensions.ARB_framebuffer_object || gl_extensions.IsGLES) {1746glBindFramebuffer(target, name);1747} else {1748#ifndef USING_GLES21749glBindFramebufferEXT(target, name);1750#endif1751}1752*cached = name;1753}1754CHECK_GL_ERROR_IF_DEBUG();1755}17561757void GLQueueRunner::fbo_unbind() {1758CHECK_GL_ERROR_IF_DEBUG();1759#ifndef USING_GLES21760if (gl_extensions.ARB_framebuffer_object || gl_extensions.IsGLES) {1761glBindFramebuffer(GL_FRAMEBUFFER, g_defaultFBO);1762} else if (gl_extensions.EXT_framebuffer_object) {1763glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, g_defaultFBO);1764}1765#else1766glBindFramebuffer(GL_FRAMEBUFFER, g_defaultFBO);1767#endif17681769#if PPSSPP_PLATFORM(IOS) && !defined(__LIBRETRO__)1770bindDefaultFBO();1771#endif17721773currentDrawHandle_ = 0;1774currentReadHandle_ = 0;1775CHECK_GL_ERROR_IF_DEBUG();1776}17771778GLRFramebuffer::~GLRFramebuffer() {1779if (handle == 0 && z_stencil_buffer == 0 && z_buffer == 0 && stencil_buffer == 0)1780return;17811782CHECK_GL_ERROR_IF_DEBUG();1783if (handle) {1784if (gl_extensions.ARB_framebuffer_object || gl_extensions.IsGLES) {1785glBindFramebuffer(GL_FRAMEBUFFER, handle);1786glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);1787glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);1788glBindFramebuffer(GL_FRAMEBUFFER, g_defaultFBO);1789glDeleteFramebuffers(1, &handle);1790#ifndef USING_GLES21791} else if (gl_extensions.EXT_framebuffer_object) {1792glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, handle);1793glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);1794glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER_EXT, 0);1795glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, g_defaultFBO);1796glDeleteFramebuffersEXT(1, &handle);1797#endif1798}1799}18001801// These can only be set when supported.1802if (z_stencil_buffer)1803glDeleteRenderbuffers(1, &z_stencil_buffer);1804if (z_buffer)1805glDeleteRenderbuffers(1, &z_buffer);1806if (stencil_buffer)1807glDeleteRenderbuffers(1, &stencil_buffer);1808CHECK_GL_ERROR_IF_DEBUG();1809}18101811std::string GLQueueRunner::StepToString(const GLRStep &step) const {1812char buffer[256];1813switch (step.stepType) {1814case GLRStepType::RENDER:1815{1816int w = step.render.framebuffer ? step.render.framebuffer->width : targetWidth_;1817int h = step.render.framebuffer ? step.render.framebuffer->height : targetHeight_;1818snprintf(buffer, sizeof(buffer), "RENDER %s %s (commands: %d, %dx%d)\n", step.tag, step.render.framebuffer ? step.render.framebuffer->Tag() : "", (int)step.commands.size(), w, h);1819break;1820}1821case GLRStepType::COPY:1822snprintf(buffer, sizeof(buffer), "COPY '%s' %s -> %s (%dx%d, %s)\n", step.tag, step.copy.src->Tag(), step.copy.dst->Tag(), step.copy.srcRect.w, step.copy.srcRect.h, GLRAspectToString((GLRAspect)step.copy.aspectMask));1823break;1824case GLRStepType::BLIT:1825snprintf(buffer, sizeof(buffer), "BLIT '%s' %s -> %s (%dx%d->%dx%d, %s)\n", step.tag, step.copy.src->Tag(), step.copy.dst->Tag(), step.blit.srcRect.w, step.blit.srcRect.h, step.blit.dstRect.w, step.blit.dstRect.h, GLRAspectToString((GLRAspect)step.blit.aspectMask));1826break;1827case GLRStepType::READBACK:1828snprintf(buffer, sizeof(buffer), "READBACK '%s' %s (%dx%d, %s)\n", step.tag, step.readback.src ? step.readback.src->Tag() : "(backbuffer)", step.readback.srcRect.w, step.readback.srcRect.h, GLRAspectToString((GLRAspect)step.readback.aspectMask));1829break;1830case GLRStepType::READBACK_IMAGE:1831snprintf(buffer, sizeof(buffer), "READBACK_IMAGE '%s' (%dx%d)\n", step.tag, step.readback_image.srcRect.w, step.readback_image.srcRect.h);1832break;1833case GLRStepType::RENDER_SKIP:1834snprintf(buffer, sizeof(buffer), "(RENDER_SKIP) %s\n", step.tag);1835break;1836default:1837buffer[0] = 0;1838break;1839}1840return std::string(buffer);1841}18421843const char *GLRAspectToString(GLRAspect aspect) {1844switch (aspect) {1845case GLR_ASPECT_COLOR: return "COLOR";1846case GLR_ASPECT_DEPTH: return "DEPTH";1847case GLR_ASPECT_STENCIL: return "STENCIL";1848default: return "N/A";1849}1850}18511852const char *RenderCommandToString(GLRRenderCommand cmd) {1853switch (cmd) {1854case GLRRenderCommand::DEPTH: return "DEPTH";1855case GLRRenderCommand::STENCIL: return "STENCIL";1856case GLRRenderCommand::BLEND: return "BLEND";1857case GLRRenderCommand::BLENDCOLOR: return "BLENDCOLOR";1858case GLRRenderCommand::LOGICOP: return "LOGICOP";1859case GLRRenderCommand::UNIFORM4I: return "UNIFORM4I";1860case GLRRenderCommand::UNIFORM4UI: return "UNIFORM4UI";1861case GLRRenderCommand::UNIFORM4F: return "UNIFORM4F";1862case GLRRenderCommand::UNIFORMMATRIX: return "UNIFORMMATRIX";1863case GLRRenderCommand::UNIFORMSTEREOMATRIX: return "UNIFORMSTEREOMATRIX";1864case GLRRenderCommand::TEXTURESAMPLER: return "TEXTURESAMPLER";1865case GLRRenderCommand::TEXTURELOD: return "TEXTURELOD";1866case GLRRenderCommand::VIEWPORT: return "VIEWPORT";1867case GLRRenderCommand::SCISSOR: return "SCISSOR";1868case GLRRenderCommand::RASTER: return "RASTER";1869case GLRRenderCommand::CLEAR: return "CLEAR";1870case GLRRenderCommand::INVALIDATE: return "INVALIDATE";1871case GLRRenderCommand::BINDPROGRAM: return "BINDPROGRAM";1872case GLRRenderCommand::BINDTEXTURE: return "BINDTEXTURE";1873case GLRRenderCommand::BIND_FB_TEXTURE: return "BIND_FB_TEXTURE";1874case GLRRenderCommand::BIND_VERTEX_BUFFER: return "BIND_VERTEX_BUFFER";1875case GLRRenderCommand::GENMIPS: return "GENMIPS";1876case GLRRenderCommand::DRAW: return "DRAW";1877case GLRRenderCommand::TEXTURE_SUBIMAGE: return "TEXTURE_SUBIMAGE";1878default: return "N/A";1879}1880}188118821883