<commit_before>#include "framebuffers.h"
#include "debug.h"
#include "../glx/hardext.h"
#include "init.h"
#if !defined(ANDROID) && !defined(AMIGAOS4)
#include <execinfo.h>
#endif
#include "blit.h"

//#define DEBUG
#ifdef DEBUG
#define DBG(a) a
#else
#define DBG(a)
#endif

//extern void* eglGetProcAddress(const char* name);

int npot(int n);
int wrap_npot(GLenum wrap);

void readfboBegin() {
	if (glstate->fbo.fbo_read == glstate->fbo.fbo_draw)
        return;
    DBG(printf("readfboBegin, fbo status read=%u, draw=%u, main=%u, current=%u\n", glstate->fbo.fbo_read, glstate->fbo.fbo_draw, glstate->fbo.mainfbo_fbo, glstate->fbo.current_fb);)
    LOAD_GLES2_OR_OES(glBindFramebuffer);
	GLuint fbo = glstate->fbo.fbo_read;
	if (!fbo)
		fbo = glstate->fbo.mainfbo_fbo;
	gles_glBindFramebuffer(GL_FRAMEBUFFER, fbo);
}

void readfboEnd() {
	if (glstate->fbo.fbo_read == glstate->fbo.fbo_draw)
        return;
    DBG(printf("readfboEnd, fbo status read=%u, draw=%u, main=%u, current=%u\n", glstate->fbo.fbo_read, glstate->fbo.fbo_draw, glstate->fbo.mainfbo_fbo, glstate->fbo.current_fb);)
    LOAD_GLES2_OR_OES(glBindFramebuffer);
	GLuint fbo = glstate->fbo.current_fb;
	if (!fbo)
		fbo = glstate->fbo.mainfbo_fbo;
	gles_glBindFramebuffer(GL_FRAMEBUFFER, fbo);
}

void attach_fbotexture(GLuint fbo, GLuint texture, int width, int height) {
    DBG(printf("attach_fbotexture(%d, %d)\n", fbo, texture);)
    int idx = -1;
    if(glstate->fbo.mainfbo_fbo && !fbo)
        fbo = glstate->fbo.mainfbo_fbo;
    if(glstate->fbo.tex_fbo) {
        for (int i=0; i<glstate->fbo.nbr_fbot && idx==-1; i++)
            if(glstate->fbo.tex_fbo[i].framebuffer==fbo) 
                idx = i;
    }
    if (idx!=-1) {
        glstate->fbo.tex_fbo[idx].texture = texture;
        glstate->fbo.tex_fbo[idx].width = width;
        glstate->fbo.tex_fbo[idx].height = height;
    } else {
        if(glstate->fbo.nbr_fbot == glstate->fbo.cap_fbot) {
            glstate->fbo.cap_fbot += 16;
            glstate->fbo.tex_fbo = (gltexframebuffer_t*)realloc(glstate->fbo.tex_fbo, glstate->fbo.cap_fbot*sizeof(gltexframebuffer_t));
        }
        idx = glstate->fbo.nbr_fbot++;
        glstate->fbo.tex_fbo[idx].framebuffer = fbo;
        glstate->fbo.tex_fbo[idx].texture = texture;
        glstate->fbo.tex_fbo[idx].width = width;
        glstate->fbo.tex_fbo[idx].height = height;
    }
}

void detach_fbotexture(GLuint fbo) {
    DBG(printf("detach_fbotexture(%d)\n", fbo);)
    int idx = -1;
    if(glstate->fbo.mainfbo_fbo && !fbo)
        fbo = glstate->fbo.mainfbo_fbo;
    if(glstate->fbo.tex_fbo) {
        for (int i=0; i<glstate->fbo.nbr_fbot && idx==-1; i++)
            if(glstate->fbo.tex_fbo[i].framebuffer==fbo) 
                idx = i;
    }
    if(idx==-1)
        return;
    if(idx<glstate->fbo.nbr_fbot)
        memmove(&glstate->fbo.tex_fbo[idx+1], &glstate->fbo.tex_fbo[idx], sizeof(gltexframebuffer_t)*(glstate->fbo.nbr_fbot-(idx+1)));
    
    --glstate->fbo.nbr_fbot;
}

int find_fbotexture(GLuint fbo, GLuint *texture, int *width, int *height) {
    DBG(printf("find_fbotexture(%d)", fbo);)
    int idx = -1;
    if(glstate->fbo.mainfbo_fbo && !fbo)
        fbo = glstate->fbo.mainfbo_fbo;
    if(glstate->fbo.tex_fbo) {
        for (int i=0; i<glstate->fbo.nbr_fbot && idx==-1; i++)
            if(glstate->fbo.tex_fbo[i].framebuffer==fbo) 
                idx = i;
    }
    if(width) *width=(idx==-1)?glstate->fbo.mainfbo_width:glstate->fbo.tex_fbo[idx].width;
    if(height) *height=(idx==-1)?glstate->fbo.mainfbo_height:glstate->fbo.tex_fbo[idx].height;
    if(texture) *texture=(idx==-1)?0:glstate->fbo.tex_fbo[idx].texture;
    
    DBG(printf(" => %d\n", (idx==-1)?0:glstate->fbo.tex_fbo[idx].texture);)

    return (idx==-1)?0:1;
}

void gl4es_glGenFramebuffers(GLsizei n, GLuint *ids) {
    DBG(printf("glGenFramebuffers(%i, %p)\n", n, ids);)
    LOAD_GLES2_OR_OES(glGenFramebuffers);
    GLsizei m = 0;
    while(globals4es.recyclefbo && (glstate->fbo.nbr_fbos>0) && (n-m>0)) {
        DBG(printf("Recycled 1 FBO\n");)
        ids[m++] = glstate->fbo.old_fbos[--glstate->fbo.nbr_fbos];
    }
    noerrorShim();
    if(n-m == 0)
        return;
    errorGL();
    gles_glGenFramebuffers(n-m, ids+m);
}

void gl4es_glDeleteFramebuffers(GLsizei n, GLuint *framebuffers) {
    DBG(printf("glDeleteFramebuffers(%i, %p), framebuffers[0]=%u\n", n, framebuffers, framebuffers[0]);)
    for (int i=0; i<n; i++)
        detach_fbotexture(framebuffers[i]);

    if (globals4es.recyclefbo) {
        DBG(printf("Recycling %i FBOs\n", n);)
        noerrorShim();
        if(glstate->fbo.cap_fbos == 0) {
            glstate->fbo.cap_fbos = 16;
            glstate->fbo.old_fbos = (GLuint*)malloc(glstate->fbo.cap_fbos * sizeof(GLuint));
        }
        if (glstate->fbo.nbr_fbos+n == glstate->fbo.cap_fbos) {
            glstate->fbo.cap_fbos += n;
            glstate->fbo.old_fbos = (GLuint*)realloc(glstate->fbo.old_fbos, glstate->fbo.cap_fbos *sizeof(GLuint));
        }
        memcpy(glstate->fbo.old_fbos+glstate->fbo.nbr_fbos, framebuffers, n*sizeof(GLuint));
        glstate->fbo.nbr_fbos += n;
    } else {
        LOAD_GLES2_OR_OES(glDeleteFramebuffers);
        errorGL();
        gles_glDeleteFramebuffers(n, framebuffers);
    }
}

GLboolean gl4es_glIsFramebuffer(GLuint framebuffer) {
    DBG(printf("glIsFramebuffer(%u)\n", framebuffer);)
    LOAD_GLES2_OR_OES(glIsFramebuffer);
    
    errorGL();
    return gles_glIsFramebuffer(framebuffer);
}

//#define BEFORE 1
GLenum gl4es_glCheckFramebufferStatus(GLenum target) {
#ifdef BEFORE
    GLenum result = glstate->fbo.fb_status;
    noerrorShim();
#else
    LOAD_GLES2_OR_OES(glCheckFramebufferStatus);
    
    errorGL();
    GLenum result = gles_glCheckFramebufferStatus(target);
#endif
DBG(printf("glCheckFramebufferStatus(0x%04X)=0x%04X\n", target, result);)
    return result;
}

void gl4es_glBindFramebuffer(GLenum target, GLuint framebuffer) {
    DBG(printf("glBindFramebuffer(%s, %u), list=%s, glstate->fbo.current_fb=%d (draw=%d, read=%d)\n", PrintEnum(target), framebuffer, glstate->list.active?"active":"none", glstate->fbo.current_fb, glstate->fbo.fbo_draw, glstate->fbo.fbo_read);)
	PUSH_IF_COMPILING(glBindFramebuffer);
    LOAD_GLES2_OR_OES(glBindFramebuffer);
//    LOAD_GLES2_OR_OES(glCheckFramebufferStatus);
    LOAD_GLES(glGetError);
        
    if (target == GL_FRAMEBUFFER) {
        glstate->fbo.fbo_read = framebuffer;
        glstate->fbo.fbo_draw = framebuffer;
    }
    
    if (target == GL_READ_FRAMEBUFFER) {
		glstate->fbo.fbo_read = framebuffer;
        noerrorShim();
        glstate->fbo.fb_status = GL_FRAMEBUFFER_COMPLETE;
		return;	//don't bind for now
	}
        
    if (target == GL_DRAW_FRAMEBUFFER) {
		target = GL_FRAMEBUFFER;
		glstate->fbo.fbo_draw = framebuffer;
	}
    
    if(target==GL_FRAMEBUFFER && framebuffer!=0) {
        gles_glBindFramebuffer(target, 0);
        //gles_glCheckFramebufferStatus(target);
    }
        
    glstate->fbo.current_fb = framebuffer;

    if (framebuffer==0)
        framebuffer = glstate->fbo.mainfbo_fbo;
        
    gles_glBindFramebuffer(target, framebuffer);
    GLenum err=gles_glGetError();
    errorShim(err);
    
//    glstate->fbo.fb_status = (framebuffer==0)?GL_FRAMEBUFFER_COMPLETE:gles_glCheckFramebufferStatus(target);
}
#undef BEFORE

GLenum ReadDraw_Push(GLenum target) {
    if(target==GL_FRAMEBUFFER)
        return GL_FRAMEBUFFER;
    LOAD_GLES2_OR_OES(glBindFramebuffer);
    if(target==GL_DRAW_FRAMEBUFFER) {
        if(glstate->fbo.current_fb!=glstate->fbo.fbo_draw)
            gles_glBindFramebuffer(GL_FRAMEBUFFER, (glstate->fbo.fbo_draw)?glstate->fbo.fbo_draw:glstate->fbo.mainfbo_fbo);
        return GL_FRAMEBUFFER;
    }
    if(target==GL_READ_FRAMEBUFFER) {
        if(glstate->fbo.current_fb!=glstate->fbo.fbo_read)
            gles_glBindFramebuffer(GL_FRAMEBUFFER, (glstate->fbo.fbo_read)?glstate->fbo.fbo_read:glstate->fbo.mainfbo_fbo);
        return GL_FRAMEBUFFER;
    }
    return target;
}
void ReadDraw_Pop(GLenum target) {
    if(target==GL_FRAMEBUFFER)
        return;
    LOAD_GLES2_OR_OES(glBindFramebuffer);
    if(target==GL_DRAW_FRAMEBUFFER && glstate->fbo.current_fb!=glstate->fbo.fbo_draw) {
        gles_glBindFramebuffer(GL_FRAMEBUFFER, (glstate->fbo.current_fb)?glstate->fbo.current_fb:glstate->fbo.mainfbo_fbo);
    }
    if(target==GL_READ_FRAMEBUFFER && glstate->fbo.current_fb!=glstate->fbo.fbo_read) {
        gles_glBindFramebuffer(GL_FRAMEBUFFER, (glstate->fbo.current_fb)?glstate->fbo.current_fb:glstate->fbo.mainfbo_fbo);
    }
}

void gl4es_glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture,	GLint level) {
    DBG(printf("glFramebufferTexture2D(%s, %s, %s, %u, %i) glstate->fbo.current_fb=%d (draw=%d, read=%d)\n", PrintEnum(target), PrintEnum(attachment), PrintEnum(textarget), texture, level, glstate->fbo.current_fb, glstate->fbo.fbo_draw, glstate->fbo.fbo_read);)
    static GLuint scrap_tex = 0;
    static int scrap_width = 0;
    static int scrap_height = 0;
    
    LOAD_GLES2_OR_OES(glFramebufferTexture2D);
    LOAD_GLES(glTexImage2D);
    LOAD_GLES(glBindTexture);
    LOAD_GLES(glActiveTexture);
    LOAD_GLES(glTexParameteri);

    // Ignore Color attachment 1 .. 9
    if ((attachment>=GL_COLOR_ATTACHMENT0+1) && (attachment<=GL_COLOR_ATTACHMENT0+9)) {
        errorShim(GL_INVALID_ENUM);
        return;
    }
    
    int twidth = 0, theight = 0;
    // find texture and get it's real name
    gltexture_t *tex = NULL;
    if (texture) {
        int ret;
        khint_t k;
        khash_t(tex) *list = glstate->texture.list;
        if (! list) {
            list = glstate->texture.list = kh_init(tex);
            // segfaults if we don't do a single put
            kh_put(tex, list, 1, &ret);
            kh_del(tex, list, 1);
        }
        k = kh_get(tex, list, texture);
        
        if (k == kh_end(list)){
            LOGE("LIBGL: texture for FBO not found, name=%u\n", texture);
        } else {
            tex = kh_value(list, k);
            texture = tex->glname;
            // check if texture is shrinked...
            if (tex->shrink || tex->useratio || (tex->adjust && hardext.npot==1 && !globals4es.potframebuffer)) {
                LOGD("LIBGL: %s texture for FBO\n",(tex->useratio)?"going back to npot size pot'ed":"unshrinking shrinked");
                if(tex->shrink || tex->useratio) {
                    if(tex->useratio) {
                        tex->width = tex->nwidth/tex->ratiox;
                        tex->height = tex->nheight/tex->ratioy;
                    } else {
                        tex->width *= 1<<tex->shrink;
                        tex->height *= 1<<tex->shrink;
                    }
                }
                tex->nwidth = (hardext.npot>0 || hardext.esversion>1)?tex->width:npot(tex->width);
                tex->nheight = (hardext.npot>0 || hardext.esversion>1)?tex->height:npot(tex->height);
                tex->adjustxy[0] = (float)tex->width / tex->nwidth;
                tex->adjustxy[1] = (float)tex->height / tex->nheight;
                tex->adjust=(tex->width!=tex->nwidth || tex->height!=tex->nheight);
                tex->shrink = 0; tex->useratio = 0;
                int oldactive = glstate->texture.active;
                if(oldactive) gles_glActiveTexture(GL_TEXTURE0);
                gltexture_t *bound = glstate->texture.bound[0/*glstate->texture.active*/][ENABLED_TEX2D];
                GLuint oldtex = bound->glname;
                if(hardext.npot==1 && !(wrap_npot(tex->wrap_s) && wrap_npot(tex->wrap_t))) {
                    gles_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
                    gles_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
                    tex->adjust = 0;
                }
                if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, tex->glname);
                gles_glTexImage2D(GL_TEXTURE_2D, 0, tex->format, tex->nwidth, tex->nheight, 0, tex->format, tex->type, NULL);
                if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, oldtex);
                if(oldactive) gles_glActiveTexture(GL_TEXTURE0+oldactive);
            }
            if(globals4es.potframebuffer && (npot(twidth)!=twidth || npot(theight!=theight))) {
                // check if POT size is asked
                LOGD("LIBGL: Resize to POT dimension texture for FBO\n");
                twidth = tex->nwidth = npot(tex->nwidth);
                theight = tex->nheight = npot(tex->nheight);
                tex->adjustxy[0] = (float)tex->width / tex->nwidth;
                tex->adjustxy[1] = (float)tex->height / tex->nheight;
                tex->adjust=(tex->width!=tex->nwidth || tex->height!=tex->nheight);
                int oldactive = glstate->texture.active;
                if(oldactive) gles_glActiveTexture(GL_TEXTURE0);
                gltexture_t *bound = glstate->texture.bound[0/*glstate->texture.active*/][ENABLED_TEX2D];
                GLuint oldtex = bound->glname;
                if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, tex->glname);
                gles_glTexImage2D(GL_TEXTURE_2D, 0, tex->format, tex->nwidth, tex->nheight, 0, tex->format, tex->type, NULL);
                if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, oldtex);
                if(oldactive) gles_glActiveTexture(GL_TEXTURE0+oldactive);
            }
            twidth = tex->nwidth;
            theight = tex->nheight;
 /*           if ((tex->width<32) || (tex->height<32)) {
                printf("LIBGL: enlarging too-small texture for FBO\n");
                tex->nwidth = (tex->nwidth<32)?32:tex->nwidth;
                tex->nheight = (tex->nheight<32)?32:tex->nheight;
                tex->shrink = 0;
                gltexture_t *bound = glstate->texture.bound[glstate->texture.active][ENABLED_TEX2D];
                GLuint oldtex = bound->glname;
                if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, tex->glname);
                gles_glTexImage2D(GL_TEXTURE_2D, 0, tex->format, tex->nwidth, tex->nheight, 0, tex->format, tex->type, NULL);
                if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, oldtex);
            }*/
            if (attachment == GL_COLOR_ATTACHMENT0)
                attach_fbotexture(glstate->fbo.current_fb, tex->texture, tex->width, tex->height);    // track the attached texture for glBlitFramebuffer
            DBG(printf("found texture, glname=%u, size=%ix%i(%ix%i), format/type=%s/%s\n", texture, tex->width, tex->height, tex->nwidth, tex->nheight, PrintEnum(tex->format), PrintEnum(tex->type));)
        }
    }
    
    GLenum ntarget = ReadDraw_Push(target);

    if(tex) {
        tex->binded_fbo = glstate->fbo.current_fb;
        tex->binded_attachment = attachment;
    }

    if(attachment==GL_DEPTH_ATTACHMENT /*&& hardext.depthtex==0*/) {
        noerrorShim();
        if (level!=0) return;
        if(hardext.depthtex && tex) {
            // depth texture supported!
            //check if texture needs to be re-created ad true depth texture
            if(tex->format!=GL_DEPTH_COMPONENT) {
                tex->format = GL_DEPTH_COMPONENT;
                if(tex->type!=GL_UNSIGNED_INT && tex->type!=GL_UNSIGNED_SHORT && tex->type!=GL_FLOAT) tex->type = (hardext.depth24)?GL_UNSIGNED_INT:GL_UNSIGNED_SHORT;
                tex->fpe_format = FPE_TEX_DEPTH;
                int oldactive = glstate->texture.active;
                if(oldactive) gles_glActiveTexture(GL_TEXTURE0);
                gltexture_t *bound = glstate->texture.bound[0/*glstate->texture.active*/][ENABLED_TEX2D];
                GLuint oldtex = bound->glname;
                if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, tex->glname);
                gles_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
                gles_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
                gles_glTexImage2D(GL_TEXTURE_2D, 0, tex->format, tex->nwidth, tex->nheight, 0, tex->format, tex->type, NULL);
                if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, oldtex);
                if(oldactive) gles_glActiveTexture(GL_TEXTURE0+oldactive);
            }
            // bind the depth texture...
            gles_glFramebufferTexture2D(ntarget, attachment, GL_TEXTURE_2D, texture, 0);
        } else {
            // let's create a renderbuffer and attach it instead of the (presumably) depth texture
            if(!tex->renderdepth) {
                gl4es_glGenRenderbuffers(1, &tex->renderdepth);
                gl4es_glBindRenderbuffer(GL_RENDERBUFFER, tex->renderdepth);
                gl4es_glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, twidth, theight);
                gl4es_glBindRenderbuffer(GL_RENDERBUFFER, 0);
            }
            gl4es_glFramebufferRenderbuffer(ntarget, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, tex->renderdepth);
        }
        errorGL();
        ReadDraw_Pop(target);
        return;
    }
    if(attachment==GL_DEPTH_STENCIL_ATTACHMENT /*&& hardext.depthtex==0*/) {
        noerrorShim();
        if (level!=0) return;
        // let's create a renderbuffer and attach it instead of the (presumably) depth texture
        if(hardext.depthstencil /*&& !(hardext.depthtex && tex)*/) {
            if(hardext.depthtex && tex) {
                if(tex->format!=GL_DEPTH_STENCIL) {
                    tex->format = GL_DEPTH_STENCIL;
                    tex->type = GL_UNSIGNED_INT_24_8;
                    tex->fpe_format = FPE_TEX_DEPTH; // add depth_stencil?
                    int oldactive = glstate->texture.active;
                    if(oldactive) gles_glActiveTexture(GL_TEXTURE0);
                    gltexture_t *bound = glstate->texture.bound[0/*glstate->texture.active*/][ENABLED_TEX2D];
                    GLuint oldtex = bound->glname;
                    if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, tex->glname);
                    gles_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
                    gles_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
                    gles_glTexImage2D(GL_TEXTURE_2D, 0, tex->format, tex->nwidth, tex->nheight, 0, tex->format, tex->type, NULL);
                    if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, oldtex);
                    if(oldactive) gles_glActiveTexture(GL_TEXTURE0+oldactive);
                }
                gles_glFramebufferTexture2D(ntarget, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture, 0);
                gles_glFramebufferTexture2D(ntarget, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, texture, 0);
            } else {
                if(!tex->renderdepth) {
                    gl4es_glGenRenderbuffers(1, &tex->renderdepth);
                    gl4es_glBindRenderbuffer(GL_RENDERBUFFER, tex->renderdepth);
                    gl4es_glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, twidth, theight);
                    gl4es_glBindRenderbuffer(GL_RENDERBUFFER, 0);
                }
                errorGL();
                gl4es_glFramebufferRenderbuffer(ntarget, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, tex->renderdepth);
                gl4es_glFramebufferRenderbuffer(ntarget, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, tex->renderdepth);
            }
        } else {
            if(hardext.depthtex && tex) {
                // depth texture supported!
                //check if texture needs to be re-created ad true depth texture
                if(tex->format!=GL_DEPTH_COMPONENT) {
                    tex->format = GL_DEPTH_COMPONENT;
                    if(tex->type!=GL_UNSIGNED_INT && tex->type!=GL_UNSIGNED_SHORT && tex->type!=GL_FLOAT) tex->type = (hardext.depth24)?GL_UNSIGNED_INT:GL_UNSIGNED_SHORT;
                    tex->fpe_format = FPE_TEX_DEPTH;
                    int oldactive = glstate->texture.active;
                    if(oldactive) gles_glActiveTexture(GL_TEXTURE0);
                    gltexture_t *bound = glstate->texture.bound[0/*glstate->texture.active*/][ENABLED_TEX2D];
                    GLuint oldtex = bound->glname;
                    if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, tex->glname);
                    gles_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
                    gles_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
                    gles_glTexImage2D(GL_TEXTURE_2D, 0, tex->format, tex->nwidth, tex->nheight, 0, tex->format, tex->type, NULL);
                    if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, oldtex);
                    if(oldactive) gles_glActiveTexture(GL_TEXTURE0+oldactive);
                }
                // bind the depth texture...
                gles_glFramebufferTexture2D(ntarget, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture, 0);
            } else {
                if(!tex->renderdepth) {
                    gl4es_glGenRenderbuffers(1, &tex->renderdepth);
                    gl4es_glBindRenderbuffer(GL_RENDERBUFFER, tex->renderdepth);
                    gl4es_glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, twidth, theight);
                    gl4es_glBindRenderbuffer(GL_RENDERBUFFER, 0);
                }
                gl4es_glFramebufferRenderbuffer(ntarget, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, tex->renderdepth);
            }
            if(!tex->renderstencil) {
                gl4es_glGenRenderbuffers(1, &tex->renderstencil);
                gl4es_glBindRenderbuffer(GL_RENDERBUFFER, tex->renderstencil);
                gl4es_glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, twidth, theight);
                gl4es_glBindRenderbuffer(GL_RENDERBUFFER, 0);
            }
            errorGL();
            gl4es_glFramebufferRenderbuffer(ntarget, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, tex->renderstencil);

        }
        ReadDraw_Pop(target);
        return;
    }

    twidth = twidth >> level; if(twidth<1) twidth=1;
    theight = theight >> level; if(theight<1) theight=1;
    
	if (level!=0) {
        //bind a scrap texture, we don't want level != 0 binding on GLES
        if(!scrap_tex)
            gl4es_glGenTextures(1, &scrap_tex);
        if ((scrap_width!=twidth) || (scrap_height!=theight)) {
                scrap_width = twidth;<commit_msg>Allow unbind of depth and stencil texture on FBO<commit_after>#include "framebuffers.h"
#include "debug.h"
#include "../glx/hardext.h"
#include "init.h"
#if !defined(ANDROID) && !defined(AMIGAOS4)
#include <execinfo.h>
#endif
#include "blit.h"

//#define DEBUG
#ifdef DEBUG
#define DBG(a) a
#else
#define DBG(a)
#endif

//extern void* eglGetProcAddress(const char* name);

int npot(int n);
int wrap_npot(GLenum wrap);

void readfboBegin() {
	if (glstate->fbo.fbo_read == glstate->fbo.fbo_draw)
        return;
    DBG(printf("readfboBegin, fbo status read=%u, draw=%u, main=%u, current=%u\n", glstate->fbo.fbo_read, glstate->fbo.fbo_draw, glstate->fbo.mainfbo_fbo, glstate->fbo.current_fb);)
    LOAD_GLES2_OR_OES(glBindFramebuffer);
	GLuint fbo = glstate->fbo.fbo_read;
	if (!fbo)
		fbo = glstate->fbo.mainfbo_fbo;
	gles_glBindFramebuffer(GL_FRAMEBUFFER, fbo);
}

void readfboEnd() {
	if (glstate->fbo.fbo_read == glstate->fbo.fbo_draw)
        return;
    DBG(printf("readfboEnd, fbo status read=%u, draw=%u, main=%u, current=%u\n", glstate->fbo.fbo_read, glstate->fbo.fbo_draw, glstate->fbo.mainfbo_fbo, glstate->fbo.current_fb);)
    LOAD_GLES2_OR_OES(glBindFramebuffer);
	GLuint fbo = glstate->fbo.current_fb;
	if (!fbo)
		fbo = glstate->fbo.mainfbo_fbo;
	gles_glBindFramebuffer(GL_FRAMEBUFFER, fbo);
}

void attach_fbotexture(GLuint fbo, GLuint texture, int width, int height) {
    DBG(printf("attach_fbotexture(%d, %d)\n", fbo, texture);)
    int idx = -1;
    if(glstate->fbo.mainfbo_fbo && !fbo)
        fbo = glstate->fbo.mainfbo_fbo;
    if(glstate->fbo.tex_fbo) {
        for (int i=0; i<glstate->fbo.nbr_fbot && idx==-1; i++)
            if(glstate->fbo.tex_fbo[i].framebuffer==fbo) 
                idx = i;
    }
    if (idx!=-1) {
        glstate->fbo.tex_fbo[idx].texture = texture;
        glstate->fbo.tex_fbo[idx].width = width;
        glstate->fbo.tex_fbo[idx].height = height;
    } else {
        if(glstate->fbo.nbr_fbot == glstate->fbo.cap_fbot) {
            glstate->fbo.cap_fbot += 16;
            glstate->fbo.tex_fbo = (gltexframebuffer_t*)realloc(glstate->fbo.tex_fbo, glstate->fbo.cap_fbot*sizeof(gltexframebuffer_t));
        }
        idx = glstate->fbo.nbr_fbot++;
        glstate->fbo.tex_fbo[idx].framebuffer = fbo;
        glstate->fbo.tex_fbo[idx].texture = texture;
        glstate->fbo.tex_fbo[idx].width = width;
        glstate->fbo.tex_fbo[idx].height = height;
    }
}

void detach_fbotexture(GLuint fbo) {
    DBG(printf("detach_fbotexture(%d)\n", fbo);)
    int idx = -1;
    if(glstate->fbo.mainfbo_fbo && !fbo)
        fbo = glstate->fbo.mainfbo_fbo;
    if(glstate->fbo.tex_fbo) {
        for (int i=0; i<glstate->fbo.nbr_fbot && idx==-1; i++)
            if(glstate->fbo.tex_fbo[i].framebuffer==fbo) 
                idx = i;
    }
    if(idx==-1)
        return;
    if(idx<glstate->fbo.nbr_fbot)
        memmove(&glstate->fbo.tex_fbo[idx+1], &glstate->fbo.tex_fbo[idx], sizeof(gltexframebuffer_t)*(glstate->fbo.nbr_fbot-(idx+1)));
    
    --glstate->fbo.nbr_fbot;
}

int find_fbotexture(GLuint fbo, GLuint *texture, int *width, int *height) {
    DBG(printf("find_fbotexture(%d)", fbo);)
    int idx = -1;
    if(glstate->fbo.mainfbo_fbo && !fbo)
        fbo = glstate->fbo.mainfbo_fbo;
    if(glstate->fbo.tex_fbo) {
        for (int i=0; i<glstate->fbo.nbr_fbot && idx==-1; i++)
            if(glstate->fbo.tex_fbo[i].framebuffer==fbo) 
                idx = i;
    }
    if(width) *width=(idx==-1)?glstate->fbo.mainfbo_width:glstate->fbo.tex_fbo[idx].width;
    if(height) *height=(idx==-1)?glstate->fbo.mainfbo_height:glstate->fbo.tex_fbo[idx].height;
    if(texture) *texture=(idx==-1)?0:glstate->fbo.tex_fbo[idx].texture;
    
    DBG(printf(" => %d\n", (idx==-1)?0:glstate->fbo.tex_fbo[idx].texture);)

    return (idx==-1)?0:1;
}

void gl4es_glGenFramebuffers(GLsizei n, GLuint *ids) {
    DBG(printf("glGenFramebuffers(%i, %p)\n", n, ids);)
    LOAD_GLES2_OR_OES(glGenFramebuffers);
    GLsizei m = 0;
    while(globals4es.recyclefbo && (glstate->fbo.nbr_fbos>0) && (n-m>0)) {
        DBG(printf("Recycled 1 FBO\n");)
        ids[m++] = glstate->fbo.old_fbos[--glstate->fbo.nbr_fbos];
    }
    noerrorShim();
    if(n-m == 0)
        return;
    errorGL();
    gles_glGenFramebuffers(n-m, ids+m);
}

void gl4es_glDeleteFramebuffers(GLsizei n, GLuint *framebuffers) {
    DBG(printf("glDeleteFramebuffers(%i, %p), framebuffers[0]=%u\n", n, framebuffers, framebuffers[0]);)
    for (int i=0; i<n; i++)
        detach_fbotexture(framebuffers[i]);

    if (globals4es.recyclefbo) {
        DBG(printf("Recycling %i FBOs\n", n);)
        noerrorShim();
        if(glstate->fbo.cap_fbos == 0) {
            glstate->fbo.cap_fbos = 16;
            glstate->fbo.old_fbos = (GLuint*)malloc(glstate->fbo.cap_fbos * sizeof(GLuint));
        }
        if (glstate->fbo.nbr_fbos+n == glstate->fbo.cap_fbos) {
            glstate->fbo.cap_fbos += n;
            glstate->fbo.old_fbos = (GLuint*)realloc(glstate->fbo.old_fbos, glstate->fbo.cap_fbos *sizeof(GLuint));
        }
        memcpy(glstate->fbo.old_fbos+glstate->fbo.nbr_fbos, framebuffers, n*sizeof(GLuint));
        glstate->fbo.nbr_fbos += n;
    } else {
        LOAD_GLES2_OR_OES(glDeleteFramebuffers);
        errorGL();
        gles_glDeleteFramebuffers(n, framebuffers);
    }
}

GLboolean gl4es_glIsFramebuffer(GLuint framebuffer) {
    DBG(printf("glIsFramebuffer(%u)\n", framebuffer);)
    LOAD_GLES2_OR_OES(glIsFramebuffer);
    
    errorGL();
    return gles_glIsFramebuffer(framebuffer);
}

//#define BEFORE 1
GLenum gl4es_glCheckFramebufferStatus(GLenum target) {
#ifdef BEFORE
    GLenum result = glstate->fbo.fb_status;
    noerrorShim();
#else
    LOAD_GLES2_OR_OES(glCheckFramebufferStatus);
    
    errorGL();
    GLenum result = gles_glCheckFramebufferStatus(target);
#endif
DBG(printf("glCheckFramebufferStatus(0x%04X)=0x%04X\n", target, result);)
    return result;
}

void gl4es_glBindFramebuffer(GLenum target, GLuint framebuffer) {
    DBG(printf("glBindFramebuffer(%s, %u), list=%s, glstate->fbo.current_fb=%d (draw=%d, read=%d)\n", PrintEnum(target), framebuffer, glstate->list.active?"active":"none", glstate->fbo.current_fb, glstate->fbo.fbo_draw, glstate->fbo.fbo_read);)
	PUSH_IF_COMPILING(glBindFramebuffer);
    LOAD_GLES2_OR_OES(glBindFramebuffer);
//    LOAD_GLES2_OR_OES(glCheckFramebufferStatus);
    LOAD_GLES(glGetError);
        
    if (target == GL_FRAMEBUFFER) {
        glstate->fbo.fbo_read = framebuffer;
        glstate->fbo.fbo_draw = framebuffer;
    }
    
    if (target == GL_READ_FRAMEBUFFER) {
		glstate->fbo.fbo_read = framebuffer;
        noerrorShim();
        glstate->fbo.fb_status = GL_FRAMEBUFFER_COMPLETE;
		return;	//don't bind for now
	}
        
    if (target == GL_DRAW_FRAMEBUFFER) {
		target = GL_FRAMEBUFFER;
		glstate->fbo.fbo_draw = framebuffer;
	}
    
    if(target==GL_FRAMEBUFFER && framebuffer!=0) {
        gles_glBindFramebuffer(target, 0);
        //gles_glCheckFramebufferStatus(target);
    }
        
    glstate->fbo.current_fb = framebuffer;

    if (framebuffer==0)
        framebuffer = glstate->fbo.mainfbo_fbo;
        
    gles_glBindFramebuffer(target, framebuffer);
    GLenum err=gles_glGetError();
    errorShim(err);
    
//    glstate->fbo.fb_status = (framebuffer==0)?GL_FRAMEBUFFER_COMPLETE:gles_glCheckFramebufferStatus(target);
}
#undef BEFORE

GLenum ReadDraw_Push(GLenum target) {
    if(target==GL_FRAMEBUFFER)
        return GL_FRAMEBUFFER;
    LOAD_GLES2_OR_OES(glBindFramebuffer);
    if(target==GL_DRAW_FRAMEBUFFER) {
        if(glstate->fbo.current_fb!=glstate->fbo.fbo_draw)
            gles_glBindFramebuffer(GL_FRAMEBUFFER, (glstate->fbo.fbo_draw)?glstate->fbo.fbo_draw:glstate->fbo.mainfbo_fbo);
        return GL_FRAMEBUFFER;
    }
    if(target==GL_READ_FRAMEBUFFER) {
        if(glstate->fbo.current_fb!=glstate->fbo.fbo_read)
            gles_glBindFramebuffer(GL_FRAMEBUFFER, (glstate->fbo.fbo_read)?glstate->fbo.fbo_read:glstate->fbo.mainfbo_fbo);
        return GL_FRAMEBUFFER;
    }
    return target;
}
void ReadDraw_Pop(GLenum target) {
    if(target==GL_FRAMEBUFFER)
        return;
    LOAD_GLES2_OR_OES(glBindFramebuffer);
    if(target==GL_DRAW_FRAMEBUFFER && glstate->fbo.current_fb!=glstate->fbo.fbo_draw) {
        gles_glBindFramebuffer(GL_FRAMEBUFFER, (glstate->fbo.current_fb)?glstate->fbo.current_fb:glstate->fbo.mainfbo_fbo);
    }
    if(target==GL_READ_FRAMEBUFFER && glstate->fbo.current_fb!=glstate->fbo.fbo_read) {
        gles_glBindFramebuffer(GL_FRAMEBUFFER, (glstate->fbo.current_fb)?glstate->fbo.current_fb:glstate->fbo.mainfbo_fbo);
    }
}

void gl4es_glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture,	GLint level) {
    DBG(printf("glFramebufferTexture2D(%s, %s, %s, %u, %i) glstate->fbo.current_fb=%d (draw=%d, read=%d)\n", PrintEnum(target), PrintEnum(attachment), PrintEnum(textarget), texture, level, glstate->fbo.current_fb, glstate->fbo.fbo_draw, glstate->fbo.fbo_read);)
    static GLuint scrap_tex = 0;
    static int scrap_width = 0;
    static int scrap_height = 0;
    
    LOAD_GLES2_OR_OES(glFramebufferTexture2D);
    LOAD_GLES(glTexImage2D);
    LOAD_GLES(glBindTexture);
    LOAD_GLES(glActiveTexture);
    LOAD_GLES(glTexParameteri);

    // Ignore Color attachment 1 .. 9
    if ((attachment>=GL_COLOR_ATTACHMENT0+1) && (attachment<=GL_COLOR_ATTACHMENT0+9)) {
        errorShim(GL_INVALID_ENUM);
        return;
    }
    
    int twidth = 0, theight = 0;
    // find texture and get it's real name
    gltexture_t *tex = NULL;
    if (texture) {
        int ret;
        khint_t k;
        khash_t(tex) *list = glstate->texture.list;
        if (! list) {
            list = glstate->texture.list = kh_init(tex);
            // segfaults if we don't do a single put
            kh_put(tex, list, 1, &ret);
            kh_del(tex, list, 1);
        }
        k = kh_get(tex, list, texture);
        
        if (k == kh_end(list)){
            LOGE("LIBGL: texture for FBO not found, name=%u\n", texture);
        } else {
            tex = kh_value(list, k);
            texture = tex->glname;
            // check if texture is shrinked...
            if (tex->shrink || tex->useratio || (tex->adjust && hardext.npot==1 && !globals4es.potframebuffer)) {
                LOGD("LIBGL: %s texture for FBO\n",(tex->useratio)?"going back to npot size pot'ed":"unshrinking shrinked");
                if(tex->shrink || tex->useratio) {
                    if(tex->useratio) {
                        tex->width = tex->nwidth/tex->ratiox;
                        tex->height = tex->nheight/tex->ratioy;
                    } else {
                        tex->width *= 1<<tex->shrink;
                        tex->height *= 1<<tex->shrink;
                    }
                }
                tex->nwidth = (hardext.npot>0 || hardext.esversion>1)?tex->width:npot(tex->width);
                tex->nheight = (hardext.npot>0 || hardext.esversion>1)?tex->height:npot(tex->height);
                tex->adjustxy[0] = (float)tex->width / tex->nwidth;
                tex->adjustxy[1] = (float)tex->height / tex->nheight;
                tex->adjust=(tex->width!=tex->nwidth || tex->height!=tex->nheight);
                tex->shrink = 0; tex->useratio = 0;
                int oldactive = glstate->texture.active;
                if(oldactive) gles_glActiveTexture(GL_TEXTURE0);
                gltexture_t *bound = glstate->texture.bound[0/*glstate->texture.active*/][ENABLED_TEX2D];
                GLuint oldtex = bound->glname;
                if(hardext.npot==1 && !(wrap_npot(tex->wrap_s) && wrap_npot(tex->wrap_t))) {
                    gles_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
                    gles_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
                    tex->adjust = 0;
                }
                if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, tex->glname);
                gles_glTexImage2D(GL_TEXTURE_2D, 0, tex->format, tex->nwidth, tex->nheight, 0, tex->format, tex->type, NULL);
                if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, oldtex);
                if(oldactive) gles_glActiveTexture(GL_TEXTURE0+oldactive);
            }
            if(globals4es.potframebuffer && (npot(twidth)!=twidth || npot(theight!=theight))) {
                // check if POT size is asked
                LOGD("LIBGL: Resize to POT dimension texture for FBO\n");
                twidth = tex->nwidth = npot(tex->nwidth);
                theight = tex->nheight = npot(tex->nheight);
                tex->adjustxy[0] = (float)tex->width / tex->nwidth;
                tex->adjustxy[1] = (float)tex->height / tex->nheight;
                tex->adjust=(tex->width!=tex->nwidth || tex->height!=tex->nheight);
                int oldactive = glstate->texture.active;
                if(oldactive) gles_glActiveTexture(GL_TEXTURE0);
                gltexture_t *bound = glstate->texture.bound[0/*glstate->texture.active*/][ENABLED_TEX2D];
                GLuint oldtex = bound->glname;
                if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, tex->glname);
                gles_glTexImage2D(GL_TEXTURE_2D, 0, tex->format, tex->nwidth, tex->nheight, 0, tex->format, tex->type, NULL);
                if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, oldtex);
                if(oldactive) gles_glActiveTexture(GL_TEXTURE0+oldactive);
            }
            twidth = tex->nwidth;
            theight = tex->nheight;
 /*           if ((tex->width<32) || (tex->height<32)) {
                printf("LIBGL: enlarging too-small texture for FBO\n");
                tex->nwidth = (tex->nwidth<32)?32:tex->nwidth;
                tex->nheight = (tex->nheight<32)?32:tex->nheight;
                tex->shrink = 0;
                gltexture_t *bound = glstate->texture.bound[glstate->texture.active][ENABLED_TEX2D];
                GLuint oldtex = bound->glname;
                if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, tex->glname);
                gles_glTexImage2D(GL_TEXTURE_2D, 0, tex->format, tex->nwidth, tex->nheight, 0, tex->format, tex->type, NULL);
                if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, oldtex);
            }*/
            if (attachment == GL_COLOR_ATTACHMENT0)
                attach_fbotexture(glstate->fbo.current_fb, tex->texture, tex->width, tex->height);    // track the attached texture for glBlitFramebuffer
            DBG(printf("found texture, glname=%u, size=%ix%i(%ix%i), format/type=%s/%s\n", texture, tex->width, tex->height, tex->nwidth, tex->nheight, PrintEnum(tex->format), PrintEnum(tex->type));)
        }
    }
    
    GLenum ntarget = ReadDraw_Push(target);

    if(tex) {
        tex->binded_fbo = glstate->fbo.current_fb;
        tex->binded_attachment = attachment;
    }

    if(attachment==GL_DEPTH_ATTACHMENT /*&& hardext.depthtex==0*/) {
        noerrorShim();
        if (level!=0) return;
        if(hardext.depthtex && (tex || !texture)) {
            // depth texture supported!
            //check if texture needs to be re-created ad true depth texture
            if(tex && tex->format!=GL_DEPTH_COMPONENT) {
                tex->format = GL_DEPTH_COMPONENT;
                if(tex->type!=GL_UNSIGNED_INT && tex->type!=GL_UNSIGNED_SHORT && tex->type!=GL_FLOAT) tex->type = (hardext.depth24)?GL_UNSIGNED_INT:GL_UNSIGNED_SHORT;
                tex->fpe_format = FPE_TEX_DEPTH;
                int oldactive = glstate->texture.active;
                if(oldactive) gles_glActiveTexture(GL_TEXTURE0);
                gltexture_t *bound = glstate->texture.bound[0/*glstate->texture.active*/][ENABLED_TEX2D];
                GLuint oldtex = bound->glname;
                if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, tex->glname);
                gles_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
                gles_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
                gles_glTexImage2D(GL_TEXTURE_2D, 0, tex->format, tex->nwidth, tex->nheight, 0, tex->format, tex->type, NULL);
                if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, oldtex);
                if(oldactive) gles_glActiveTexture(GL_TEXTURE0+oldactive);
            }
            // bind the depth texture...
            gles_glFramebufferTexture2D(ntarget, attachment, GL_TEXTURE_2D, texture, 0);
        } else {
            // let's create a renderbuffer and attach it instead of the (presumably) depth texture
            if(tex && !tex->renderdepth) {
                gl4es_glGenRenderbuffers(1, &tex->renderdepth);
                gl4es_glBindRenderbuffer(GL_RENDERBUFFER, tex->renderdepth);
                gl4es_glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, twidth, theight);
                gl4es_glBindRenderbuffer(GL_RENDERBUFFER, 0);
            }
            gl4es_glFramebufferRenderbuffer(ntarget, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, tex?tex->renderdepth:0);
        }
        errorGL();
        ReadDraw_Pop(target);
        return;
    }
    if(attachment==GL_DEPTH_STENCIL_ATTACHMENT /*&& hardext.depthtex==0*/) {
        noerrorShim();
        if (level!=0) return;
        // let's create a renderbuffer and attach it instead of the (presumably) depth texture
        if(hardext.depthstencil /*&& !(hardext.depthtex && tex)*/) {
            if(hardext.depthtex && (tex || !texture)) {
                if(tex && tex->format!=GL_DEPTH_STENCIL) {
                    tex->format = GL_DEPTH_STENCIL;
                    tex->type = GL_UNSIGNED_INT_24_8;
                    tex->fpe_format = FPE_TEX_DEPTH; // add depth_stencil?
                    int oldactive = glstate->texture.active;
                    if(oldactive) gles_glActiveTexture(GL_TEXTURE0);
                    gltexture_t *bound = glstate->texture.bound[0/*glstate->texture.active*/][ENABLED_TEX2D];
                    GLuint oldtex = bound->glname;
                    if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, tex->glname);
                    gles_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
                    gles_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
                    gles_glTexImage2D(GL_TEXTURE_2D, 0, tex->format, tex->nwidth, tex->nheight, 0, tex->format, tex->type, NULL);
                    if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, oldtex);
                    if(oldactive) gles_glActiveTexture(GL_TEXTURE0+oldactive);
                }
                gles_glFramebufferTexture2D(ntarget, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture, 0);
                gles_glFramebufferTexture2D(ntarget, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, texture, 0);
            } else {
                if(tex && !tex->renderdepth) {
                    gl4es_glGenRenderbuffers(1, &tex->renderdepth);
                    gl4es_glBindRenderbuffer(GL_RENDERBUFFER, tex->renderdepth);
                    gl4es_glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, twidth, theight);
                    gl4es_glBindRenderbuffer(GL_RENDERBUFFER, 0);
                }
                errorGL();
                gl4es_glFramebufferRenderbuffer(ntarget, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, tex?tex->renderdepth:0);
                gl4es_glFramebufferRenderbuffer(ntarget, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, tex?tex->renderdepth:0);
            }
        } else {
            if(hardext.depthtex && (tex || !texture)) {
                // depth texture supported!
                //check if texture needs to be re-created ad true depth texture
                if(tex && tex->format!=GL_DEPTH_COMPONENT) {
                    tex->format = GL_DEPTH_COMPONENT;
                    if(tex->type!=GL_UNSIGNED_INT && tex->type!=GL_UNSIGNED_SHORT && tex->type!=GL_FLOAT) tex->type = (hardext.depth24)?GL_UNSIGNED_INT:GL_UNSIGNED_SHORT;
                    tex->fpe_format = FPE_TEX_DEPTH;
                    int oldactive = glstate->texture.active;
                    if(oldactive) gles_glActiveTexture(GL_TEXTURE0);
                    gltexture_t *bound = glstate->texture.bound[0/*glstate->texture.active*/][ENABLED_TEX2D];
                    GLuint oldtex = bound->glname;
                    if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, tex->glname);
                    gles_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
                    gles_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
                    gles_glTexImage2D(GL_TEXTURE_2D, 0, tex->format, tex->nwidth, tex->nheight, 0, tex->format, tex->type, NULL);
                    if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, oldtex);
                    if(oldactive) gles_glActiveTexture(GL_TEXTURE0+oldactive);
                }
                // bind the depth texture...
                gles_glFramebufferTexture2D(ntarget, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture, 0);
            } else {
                if(tex && !tex->renderdepth) {
                    gl4es_glGenRenderbuffers(1, &tex->renderdepth);
                    gl4es_glBindRenderbuffer(GL_RENDERBUFFER, tex->renderdepth);
                    gl4es_glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, twidth, theight);
                    gl4es_glBindRenderbuffer(GL_RENDERBUFFER, 0);
                }
                gl4es_glFramebufferRenderbuffer(ntarget, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, tex?tex->renderdepth:0);
            }
            if(tex && !tex->renderstencil) {
                gl4es_glGenRenderbuffers(1, &tex->renderstencil);
                gl4es_glBindRenderbuffer(GL_RENDERBUFFER, tex->renderstencil);
                gl4es_glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, twidth, theight);
                gl4es_glBindRenderbuffer(GL_RENDERBUFFER, 0);
            }
            errorGL();
            gl4es_glFramebufferRenderbuffer(ntarget, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, tex?tex->renderstencil:0);

        }
        ReadDraw_Pop(target);
        return;
    }

    twidth = twidth >> level; if(twidth<1) twidth=1;
    theight = theight >> level; if(theight<1) theight=1;
    
	if (level!=0) {
        //bind a scrap texture, we don't want level != 0 binding on GLES
        if(!scrap_tex)
            gl4es_glGenTextures(1, &scrap_tex);
        if ((scrap_width!=twidth) || (scrap_height!=theight)) {
                scrap_width = twidth;
{'fraction_non_alphanumeric': 0.133654786241223, 'fraction_numerical': 0.011087903297484668, 'mean_word_length': 2.645605508302957, 'pattern_counts': {'":': 4, '<': 37, '<?xml version=': 0, '>': 623, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3704014', 'n_tokens_mistral': 16404, 'n_tokens_neox': 15750, 'n_words': 2267}