Files
questshock/android/gl4es-src/src/gl/framebuffers.c
T
ml d2dc58391b
build / build (push) Successful in 2m26s
Add controller input and a laser-pointer-driven menu quad
- xr_input.c/h: an OpenXR action set (aim pose, trigger/select click, a
  menu-toggle button) plus ray/quad hit-testing, and a visible
  laser-pointer line drawn from the controller toward the hit point.
- xr_menu.c/h + MenuOverlay.java: a second composition-layer quad,
  toggled by a controller button, showing an off-screen, never-attached
  Android View tree (one "Keyboard" button so far) driven by synthetic
  touch events computed from the laser ray's hit UV.
- Migrate gl4es from a prebuilt binary baked into the Docker build-image
  to a vendored source snapshot (android/gl4es-src/) compiled from
  source at APK build time via CMake add_subdirectory(), patched through
  a new android/gl4es-patches/ (mirrors the existing engine-patches/
  pattern). This was needed to track down and fix a bug hit while
  building the menu: gl4es's fixed-pipeline emulation (fpe.c) was
  unconditionally substituting its own shader onto the menu's draw call
  (fpe_ReleventState() always sets alphafunc to a nonzero sentinel, so
  its fpe_IsEmpty() check could never see the state as empty), making
  the menu quad show the game's own rendering instead of its own
  content. Fixed by routing the menu's blit through a real
  glBlitFramebuffer() call instead of a shader-based draw, which gl4es's
  fpe.c has nothing to intercept - documented in README.md's new
  "Debugging notes" section.
- Renumber android/engine-patches/ to close the gap left by removing an
  unrelated diagnostic-only patch, and strip investigation-journal
  comments and dead diagnostic code (temporary tracing, env-var probes)
  left over from finding the bug above.
- Restructure README.md into numbered sections, document every
  engine/gl4es patch, add Android Studio dev/testing-cycle instructions,
  and generalize Quest-specific wording to any OpenXR headset. Update
  NOTICE.txt to match (gl4es is now vendored/patched source, not a
  prebuilt binary; add the OpenXR-SDK loader).
2026-08-02 06:27:49 +02:00

1817 lines
81 KiB
C

#include "framebuffers.h"
#include "../glx/hardext.h"
#include "blit.h"
#include "debug.h"
#include "fpe.h"
#include "gl4es.h"
#include "glstate.h"
#include "init.h"
#include "loader.h"
//#define DEBUG
#ifdef DEBUG
#define DBG(a) a
#else
#define DBG(a)
#endif
KHASH_MAP_IMPL_INT(renderbufferlist_t, glrenderbuffer_t *);
KHASH_MAP_IMPL_INT(framebufferlist_t, glframebuffer_t *);
//extern void* eglGetProcAddress(const char* name);
int npot(int n);
int wrap_npot(GLenum wrap);
glframebuffer_t* find_framebuffer(GLuint framebuffer) {
// Get a framebuffer based on ID
if (framebuffer == 0) return glstate->fbo.fbo_0; // NULL or fbo_0 ?
khint_t k;
khash_t(framebufferlist_t) *list = glstate->fbo.framebufferlist;
k = kh_get(framebufferlist_t, list, framebuffer);
if (k != kh_end(list)){
return kh_value(list, k);
}
return NULL;
}
glframebuffer_t* get_framebuffer(GLenum target) {
switch (target) {
case GL_FRAMEBUFFER: return glstate->fbo.current_fb;
case GL_READ_FRAMEBUFFER: return glstate->fbo.fbo_read;
case GL_DRAW_FRAMEBUFFER: return glstate->fbo.fbo_draw;
}
return NULL;
}
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->id, glstate->fbo.fbo_draw->id, glstate->fbo.mainfbo_fbo, glstate->fbo.current_fb->id);)
if(glstate->fbo.fbo_read==glstate->fbo.current_fb)
return;
glstate->fbo.current_fb = glstate->fbo.fbo_read;
GLuint fbo = glstate->fbo.fbo_read->id;
if (!fbo)
fbo = glstate->fbo.mainfbo_fbo;
LOAD_GLES2_OR_OES(glBindFramebuffer);
gles_glBindFramebuffer(GL_FRAMEBUFFER, fbo);
}
void readfboEnd() {
if (glstate->fbo.fbo_read->id == glstate->fbo.fbo_draw->id)
return;
DBG(printf("readfboEnd, fbo status read=%p, draw=%p, main=%u, current=%p\n", glstate->fbo.fbo_read, glstate->fbo.fbo_draw, glstate->fbo.mainfbo_fbo, glstate->fbo.current_fb);)
if(glstate->fbo.fbo_draw==glstate->fbo.current_fb)
return;
glstate->fbo.current_fb = glstate->fbo.fbo_draw;
GLuint fbo = glstate->fbo.fbo_draw->id;
if (!fbo)
fbo = glstate->fbo.mainfbo_fbo;
LOAD_GLES2_OR_OES(glBindFramebuffer);
gles_glBindFramebuffer(GL_FRAMEBUFFER, fbo);
}
glrenderbuffer_t* find_renderbuffer(GLuint renderbuffer) {
// Get a renderbuffer based on ID
if (renderbuffer == 0) return glstate->fbo.default_rb;
khint_t k;
khash_t(renderbufferlist_t) *list = glstate->fbo.renderbufferlist;
k = kh_get(renderbufferlist_t, list, renderbuffer);
if (k != kh_end(list)){
return kh_value(list, k);
}
return NULL;
}
void APIENTRY_GL4ES gl4es_glGenFramebuffers(GLsizei n, GLuint *ids) {
DBG(printf("glGenFramebuffers(%i, %p)\n", n, ids);)
LOAD_GLES2_OR_OES(glGenFramebuffers);
GLsizei m = 0;
while(glstate->fbo.old && (glstate->fbo.old->nbr>0) && (n-m>0)) {
DBG(printf("Recycled 1 FBO\n");)
ids[m++] = glstate->fbo.old->fbos[--glstate->fbo.old->nbr];
}
noerrorShim();
if(n-m) {
errorGL();
gles_glGenFramebuffers(n-m, ids+m);
}
// track the framebuffers...
int ret;
khint_t k;
khash_t(framebufferlist_t) *list = glstate->fbo.framebufferlist;
for(int i=0; i<n; ++i) {
k = kh_put(framebufferlist_t, list, ids[i], &ret);
glframebuffer_t *fb = kh_value(list, k) = malloc(sizeof(glframebuffer_t));
memset(fb, 0, sizeof(glframebuffer_t));
fb->id = ids[i];
fb->n_draw = 0; // correct?
}
}
void APIENTRY_GL4ES gl4es_glDeleteFramebuffers(GLsizei n, GLuint *framebuffers) {
DBG(printf("glDeleteFramebuffers(%i, %p), framebuffers[0]=%u\n", n, framebuffers, framebuffers[0]);)
// delete tracking
if (glstate->fbo.framebufferlist)
for (int i=0; i<n; i++) {
khint_t k;
glframebuffer_t *fb;
for (int i = 0; i < n; i++) {
GLuint t = framebuffers[i];
if(t) {
k = kh_get(framebufferlist_t, glstate->fbo.framebufferlist, t);
if (k != kh_end(glstate->fbo.framebufferlist)) {
fb = kh_value(glstate->fbo.framebufferlist, k);
// detach texture...
for(int j=0; j<MAX_DRAW_BUFFERS; ++j) {
if(fb->color[j] && fb->t_color[j]!=GL_RENDERBUFFER) {
gltexture_t *tex = gl4es_getTexture(fb->t_color[j], fb->color[j]);
if(tex) {
tex->binded_fbo = 0;
tex->binded_attachment = 0;
}
}
}
if(fb->depth && fb->t_depth!=GL_RENDERBUFFER) {
gltexture_t *tex = gl4es_getTexture(fb->t_depth, fb->depth);
if(tex) {
tex->binded_fbo = 0;
tex->binded_attachment = 0;
tex->renderdepth = 0;
}
}
if(fb->stencil && fb->t_stencil!=GL_RENDERBUFFER) {
gltexture_t *tex = gl4es_getTexture(fb->t_stencil, fb->stencil);
if(tex) {
tex->binded_fbo = 0;
tex->binded_attachment = 0;
tex->renderstencil = 0;
}
}
if (glstate->fbo.current_fb == fb) {
glstate->fbo.current_fb = 0;
}
if (glstate->fbo.fbo_read == fb) {
glstate->fbo.fbo_read = 0;
}
if (glstate->fbo.fbo_draw == fb) {
glstate->fbo.fbo_draw = 0;
}
free(fb);
kh_del(framebufferlist_t, glstate->fbo.framebufferlist, k);
}
}
}
}
if (globals4es.recyclefbo) {
DBG(printf("Recycling %i FBOs\n", n);)
noerrorShim();
if(glstate->fbo.old->cap == 0) {
glstate->fbo.old->cap = 16;
glstate->fbo.old->fbos = (GLuint*)malloc(glstate->fbo.old->cap * sizeof(GLuint));
}
if (glstate->fbo.old->nbr+n > glstate->fbo.old->cap) {
glstate->fbo.old->cap += n;
glstate->fbo.old->fbos = (GLuint*)realloc(glstate->fbo.old->fbos, glstate->fbo.old->cap *sizeof(GLuint));
}
memcpy(glstate->fbo.old->fbos+glstate->fbo.old->nbr, framebuffers, n*sizeof(GLuint));
glstate->fbo.old->nbr += n;
} else {
LOAD_GLES2_OR_OES(glDeleteFramebuffers);
errorGL();
gles_glDeleteFramebuffers(n, framebuffers);
}
}
GLboolean APIENTRY_GL4ES gl4es_glIsFramebuffer(GLuint framebuffer) {
DBG(printf("glIsFramebuffer(%u)\n", framebuffer);)
LOAD_GLES2_OR_OES(glIsFramebuffer);
errorGL();
return find_framebuffer(framebuffer)!=NULL;
}
GLenum APIENTRY_GL4ES gl4es_glCheckFramebufferStatus(GLenum target) {
GLenum result;
if(glstate->fbo.internal) {
result = glstate->fbo.fb_status;
noerrorShim();
} else {
LOAD_GLES2_OR_OES(glCheckFramebufferStatus);
errorGL();
GLenum rtarget = target;
if(target==GL_READ_FRAMEBUFFER)
return GL_FRAMEBUFFER_COMPLETE; // cheating here
if(target==GL_DRAW_FRAMEBUFFER)
rtarget = GL_FRAMEBUFFER;
result = gles_glCheckFramebufferStatus(rtarget);
}
DBG(printf("glCheckFramebufferStatus(0x%04X)=0x%04X\n", target, result);)
return result;
}
void APIENTRY_GL4ES 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->id, glstate->fbo.fbo_draw->id, glstate->fbo.fbo_read->id);)
PUSH_IF_COMPILING(glBindFramebuffer);
LOAD_GLES2_OR_OES(glBindFramebuffer);
// LOAD_GLES2_OR_OES(glCheckFramebufferStatus);
LOAD_GLES(glGetError);
glframebuffer_t *fb = find_framebuffer(framebuffer);
if(!fb) {
errorShim(GL_INVALID_VALUE);
return;
}
if (target == GL_FRAMEBUFFER) {
glstate->fbo.fbo_read = fb;
glstate->fbo.fbo_draw = fb;
}
if (target == GL_READ_FRAMEBUFFER) {
glstate->fbo.fbo_read = fb;
noerrorShim();
glstate->fbo.fb_status = GL_FRAMEBUFFER_COMPLETE;
glstate->fbo.internal = 1;
return; //don't bind for now
} else glstate->fbo.internal = 0;
if (target == GL_DRAW_FRAMEBUFFER) {
target = GL_FRAMEBUFFER;
glstate->fbo.fbo_draw = fb;
}
if (target != GL_FRAMEBUFFER) {
errorShim(GL_INVALID_ENUM);
return;
}
if(framebuffer==0)
framebuffer = glstate->fbo.mainfbo_fbo;
glstate->fbo.current_fb = fb;
gles_glBindFramebuffer(target, framebuffer);
GLenum err=gles_glGetError();
errorShim(err);
// glstate->fbo.fb_status = (framebuffer==0)?GL_FRAMEBUFFER_COMPLETE:gles_glCheckFramebufferStatus(target);
}
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->id)?glstate->fbo.fbo_draw->id: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->id)?glstate->fbo.fbo_read->id: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->id)?glstate->fbo.current_fb->id: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->id)?glstate->fbo.current_fb->id:glstate->fbo.mainfbo_fbo);
}
}
void SetAttachment(glframebuffer_t* fb, GLenum attachment, GLenum atttarget, GLuint att, int level)
{
switch (attachment) {
case GL_COLOR_ATTACHMENT0:
case GL_COLOR_ATTACHMENT1:
case GL_COLOR_ATTACHMENT2:
case GL_COLOR_ATTACHMENT3:
case GL_COLOR_ATTACHMENT4:
case GL_COLOR_ATTACHMENT5:
case GL_COLOR_ATTACHMENT6:
case GL_COLOR_ATTACHMENT7:
case GL_COLOR_ATTACHMENT8:
case GL_COLOR_ATTACHMENT9:
case GL_COLOR_ATTACHMENT10:
case GL_COLOR_ATTACHMENT11:
case GL_COLOR_ATTACHMENT12:
case GL_COLOR_ATTACHMENT13:
case GL_COLOR_ATTACHMENT14:
case GL_COLOR_ATTACHMENT15:
fb->color[attachment - GL_COLOR_ATTACHMENT0] = att;
fb->l_color[attachment - GL_COLOR_ATTACHMENT0] = level;
fb->t_color[attachment - GL_COLOR_ATTACHMENT0] = atttarget;
break;
case GL_DEPTH_ATTACHMENT:
fb->depth = att;
fb->t_depth = atttarget;
fb->l_depth = 0;
break;
case GL_STENCIL_ATTACHMENT:
fb->stencil = att;
fb->t_stencil = atttarget;
fb->l_stencil = 0;
break;
case GL_DEPTH_STENCIL_ATTACHMENT:
fb->depth = att;
fb->t_depth = atttarget;
fb->l_depth = 0;
fb->stencil = att;
fb->t_stencil = atttarget;
fb->l_stencil = 0;
break;
}
}
GLuint GetAttachment(glframebuffer_t* fb, GLenum attachment) {
switch (attachment) {
case GL_COLOR_ATTACHMENT0:
case GL_COLOR_ATTACHMENT1:
case GL_COLOR_ATTACHMENT2:
case GL_COLOR_ATTACHMENT3:
case GL_COLOR_ATTACHMENT4:
case GL_COLOR_ATTACHMENT5:
case GL_COLOR_ATTACHMENT6:
case GL_COLOR_ATTACHMENT7:
case GL_COLOR_ATTACHMENT8:
case GL_COLOR_ATTACHMENT9:
case GL_COLOR_ATTACHMENT10:
case GL_COLOR_ATTACHMENT11:
case GL_COLOR_ATTACHMENT12:
case GL_COLOR_ATTACHMENT13:
case GL_COLOR_ATTACHMENT14:
case GL_COLOR_ATTACHMENT15:
return fb->color[attachment - GL_COLOR_ATTACHMENT0];
case GL_DEPTH_ATTACHMENT:
return fb->depth;
case GL_STENCIL_ATTACHMENT:
return fb->stencil;
case GL_DEPTH_STENCIL_ATTACHMENT:
// is that possible?
return fb->depth;
}
return 0;
}
GLenum GetAttachmentType(glframebuffer_t* fb, GLenum attachment) {
switch (attachment) {
case GL_COLOR_ATTACHMENT0:
case GL_COLOR_ATTACHMENT1:
case GL_COLOR_ATTACHMENT2:
case GL_COLOR_ATTACHMENT3:
case GL_COLOR_ATTACHMENT4:
case GL_COLOR_ATTACHMENT5:
case GL_COLOR_ATTACHMENT6:
case GL_COLOR_ATTACHMENT7:
case GL_COLOR_ATTACHMENT8:
case GL_COLOR_ATTACHMENT9:
case GL_COLOR_ATTACHMENT10:
case GL_COLOR_ATTACHMENT11:
case GL_COLOR_ATTACHMENT12:
case GL_COLOR_ATTACHMENT13:
case GL_COLOR_ATTACHMENT14:
case GL_COLOR_ATTACHMENT15:
return fb->t_color[attachment - GL_COLOR_ATTACHMENT0];
case GL_DEPTH_ATTACHMENT:
return fb->t_depth;
case GL_STENCIL_ATTACHMENT:
return fb->t_stencil;
case GL_DEPTH_STENCIL_ATTACHMENT:
return fb->t_depth;
}
return 0;
}
int GetAttachmentLevel(glframebuffer_t* fb, GLenum attachment) {
switch (attachment) {
case GL_COLOR_ATTACHMENT0:
case GL_COLOR_ATTACHMENT1:
case GL_COLOR_ATTACHMENT2:
case GL_COLOR_ATTACHMENT3:
case GL_COLOR_ATTACHMENT4:
case GL_COLOR_ATTACHMENT5:
case GL_COLOR_ATTACHMENT6:
case GL_COLOR_ATTACHMENT7:
case GL_COLOR_ATTACHMENT8:
case GL_COLOR_ATTACHMENT9:
case GL_COLOR_ATTACHMENT10:
case GL_COLOR_ATTACHMENT11:
case GL_COLOR_ATTACHMENT12:
case GL_COLOR_ATTACHMENT13:
case GL_COLOR_ATTACHMENT14:
case GL_COLOR_ATTACHMENT15:
return fb->l_color[attachment - GL_COLOR_ATTACHMENT0];
case GL_DEPTH_ATTACHMENT:
return fb->l_depth;
case GL_STENCIL_ATTACHMENT:
return fb->l_stencil;
case GL_DEPTH_STENCIL_ATTACHMENT:
return fb->l_depth;
}
return 0;
}
void APIENTRY_GL4ES 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->id, glstate->fbo.fbo_draw->id, glstate->fbo.fbo_read->id);)
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);
glframebuffer_t *fb = get_framebuffer(target);
if(!fb) {
errorShim(GL_INVALID_ENUM);
return;
}
if( !(attachment>=GL_COLOR_ATTACHMENT0 && attachment<(GL_COLOR_ATTACHMENT0+hardext.maxcolorattach))
&& attachment!=GL_DEPTH_ATTACHMENT
&& attachment!=GL_STENCIL_ATTACHMENT
&& attachment!=GL_DEPTH_STENCIL_ATTACHMENT) {
errorShim(GL_INVALID_ENUM);
return;
}
int twidth = 0, theight = 0;
// find texture and get it's real name
gltexture_t *tex = NULL;
if (texture) {
tex = gl4es_getTexture(textarget, texture);
if (!tex) {
LOGE("texture for FBO not found, name=%u\n", texture);
} else {
texture = tex->glname;
tex->fbtex_ratio = (globals4es.fbtexscale > 0.0f) ? globals4es.fbtexscale : 0.0f;
// check if texture is shrinked or if fb texture is being scaled...
if (globals4es.fbtexscale > 0.0f || tex->shrink || tex->useratio || (tex->adjust && (hardext.npot==1 || hardext.npot==2) && !globals4es.potframebuffer)) {
LOGD("%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;
}
}
// Use FBO Ratio
if (tex->fbtex_ratio > 0.0f) {
tex->width *= tex->fbtex_ratio;
tex->height *= tex->fbtex_ratio;
}
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 (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);
}
int need_change = (globals4es.potframebuffer && (npot(twidth)!=twidth || npot(theight)!=theight))?1:0;
if((tex->type==GL_FLOAT && !hardext.floatfbo) || (tex->type==GL_HALF_FLOAT_OES && !hardext.halffloatfbo)) {
need_change += 2;
tex->type = GL_UNSIGNED_BYTE;
}
if(tex->format==GL_BGRA && (globals4es.nobgra || !hardext.bgra8888)) {
if(need_change<2) need_change += 2;
tex->format = GL_RGBA;
}
if(need_change) {
// check if POT size is asked
LOGD("Recreate a texture for a FBO (%s%s%s)\n", (need_change&1)?"POT":"", (need_change==3)?" & ":"", (need_change&2)?"Format/Type":"");
if(need_change&1) {
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);
}*/
fb->width = twidth;
fb->height = theight;
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);
GLuint old_attachment = GetAttachment(fb, attachment);
GLuint old_attachment_type = GetAttachmentType(fb, attachment);
if(old_attachment) {
gltexture_t* old = gl4es_getTexture(old_attachment_type, old_attachment);
if(old) {
DBG(printf("Detach Texture %d from FBO %d as Attachement %s\n", old->glname, old->binded_fbo, PrintEnum(old->binded_attachment));)
old->binded_fbo = 0;
old->binded_attachment = 0;
} else {
DBG(printf("Old attachement %s for FBO %d not found in textures\n", PrintEnum(attachment), fb->id);)
}
}
if(tex) {
DBG(printf("Attach Texture %d to FBO %d as Attachement %s\n", tex->glname, fb->id, PrintEnum(attachment));)
tex->binded_fbo = fb->id;
tex->binded_attachment = attachment;
}
if ((old_attachment_type == textarget) && (old_attachment == (tex?tex->texture:texture)))
{
// no need to reattach
noerrorShim();
return;
}
SetAttachment(fb, attachment, textarget, tex?tex->texture:texture, level);
if(attachment>=GL_COLOR_ATTACHMENT0 && attachment<(GL_COLOR_ATTACHMENT0+hardext.maxcolorattach) && tex) {
gltexture_t *bound = glstate->texture.bound[0/*glstate->texture.active*/][ENABLED_TEX2D];
if((hardext.npot==1 || hardext.npot==2) && (!tex->actual.wrap_s || !tex->actual.wrap_t || !wrap_npot(tex->actual.wrap_s) || !wrap_npot(tex->actual.wrap_t))) {
tex->sampler.wrap_s = tex->sampler.wrap_t = GL_CLAMP_TO_EDGE;
tex->adjust = 0;
}
//npot==2 and 3 should support that, but let's ignore that for now and force no mipmap for texture attached to fbo...
if(!tex->actual.min_filter || !minmag_npot(tex->actual.min_filter) || tex->actual.min_filter!=tex->sampler.min_filter) {
if(hardext.npot<2) {
tex->sampler.min_filter = minmag_forcenpot(tex->sampler.min_filter);
tex->adjust = 0;
tex->mipmap_need = 0;
tex->mipmap_auto = 0;
}
}
realize_1texture(map_tex_target(textarget), -1, tex, NULL);
}
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 as true depth texture
if(tex && !(tex->format==GL_DEPTH_COMPONENT || tex->format==GL_DEPTH_STENCIL)) {
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;
realize_textures(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 (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_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);
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_STENCIL_ATTACHMENT /*&& hardext.depthtex==0*/) {
noerrorShim();
if (level!=0) return;
// this one is tricky, as it can be GL_DEPTH_STENCIL with DEPTH+DEPTH_STENCIL extension of GL_STENCIL_INDEX8 with STENCIL extension
// and having DEPTH+DEPTH_STENCIL extension doesn't grant STENCIL8 texture extension!
if((tex || !texture) && (hardext.stenciltex || (hardext.depthtex && hardext.depthstencil))) {
// depth texture supported, so are stencil one then!
//check if texture needs to be re-created as true depth texture
if(tex && !(tex->format==GL_STENCIL_INDEX8 || tex->format==GL_DEPTH_STENCIL)) {
if(tex->format==GL_DEPTH_ATTACHMENT) {
//TODO: need to create a new texture, as the depth one is probably used
gl4es_glGenTextures(1, &texture);
realize_textures(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;
int nwidth = tex->nwidth;
int nheight = tex->nheight;
tex = gl4es_getTexture(textarget, texture);
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_MIN_FILTER, GL_NEAREST);
gles_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
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->format = (hardext.stenciltex)?GL_STENCIL_INDEX8:GL_DEPTH_STENCIL;
tex->type = (hardext.stenciltex)?GL_UNSIGNED_BYTE:GL_UNSIGNED_INT_24_8;
gl4es_glTexImage2D(GL_TEXTURE_2D, 0, tex->format, nwidth, nheight, 0, tex->format, tex->type, NULL);
if (oldtex!=tex->glname) gles_glBindTexture(GL_TEXTURE_2D, oldtex);
if(oldactive) gles_glActiveTexture(GL_TEXTURE0+oldactive);
} else {
tex->format = GL_STENCIL_INDEX8;
if(tex->type!=GL_UNSIGNED_BYTE && tex->type!=GL_UNSIGNED_SHORT && tex->type!=GL_FLOAT) tex->type = GL_UNSIGNED_BYTE;
tex->fpe_format = FPE_TEX_DEPTH;
realize_textures(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 (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_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);
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 stencil texture...
gles_glFramebufferTexture2D(ntarget, attachment, GL_TEXTURE_2D, texture, 0);
} else {
// let's create a renderbuffer and attach it instead of the (presumably) stencil texture
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);
}
gl4es_glFramebufferRenderbuffer(ntarget, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, tex?tex->renderstencil: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_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);
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_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);
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;
scrap_height = theight;
gltexture_t *bound = glstate->texture.bound[glstate->texture.active][ENABLED_TEX2D];
GLuint oldtex = bound->glname;
if (oldtex!=scrap_tex) gles_glBindTexture(GL_TEXTURE_2D, scrap_tex);
gles_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, scrap_width, scrap_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
if (oldtex!=scrap_tex) gles_glBindTexture(GL_TEXTURE_2D, oldtex);
}
texture = scrap_tex;
}
errorGL();
GLenum realtarget = GL_TEXTURE_2D;
if(textarget>=GL_TEXTURE_CUBE_MAP_POSITIVE_X && textarget<GL_TEXTURE_CUBE_MAP_POSITIVE_X+6)
realtarget = textarget;
gles_glFramebufferTexture2D(ntarget, attachment, realtarget, texture, 0);
DBG(CheckGLError(1);)
ReadDraw_Pop(target);
}
void APIENTRY_GL4ES gl4es_glFramebufferTexture1D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) {
gl4es_glFramebufferTexture2D(target, attachment, textarget, texture, level);
}
void APIENTRY_GL4ES gl4es_glFramebufferTexture3D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint layer) {
(void)layer;
gl4es_glFramebufferTexture2D(target, attachment, textarget, texture, level);
}
void APIENTRY_GL4ES gl4es_glGenRenderbuffers(GLsizei n, GLuint *renderbuffers) {
DBG(printf("glGenRenderbuffers(%i, %p)\n", n, renderbuffers);)
LOAD_GLES2_OR_OES(glGenRenderbuffers);
errorGL();
gles_glGenRenderbuffers(n, renderbuffers);
// track the renderbuffers...
int ret;
khint_t k;
khash_t(renderbufferlist_t) *list = glstate->fbo.renderbufferlist;
for(int i=0; i<n; ++i) {
k = kh_put(renderbufferlist_t, list, renderbuffers[i], &ret);
glrenderbuffer_t *rend = kh_value(list, k) = malloc(sizeof(glrenderbuffer_t));
memset(rend, 0, sizeof(glrenderbuffer_t));
rend->renderbuffer = renderbuffers[i];
}
}
void APIENTRY_GL4ES gl4es_glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) {
DBG(printf("glFramebufferRenderbuffer(%s, %s, %s, %u)\n", PrintEnum(target), PrintEnum(attachment), PrintEnum(renderbuffertarget), renderbuffer);)
LOAD_GLES2_OR_OES(glFramebufferRenderbuffer);
LOAD_GLES2_OR_OES(glGetFramebufferAttachmentParameteriv);
LOAD_GLES(glGetError);
glframebuffer_t *fb = get_framebuffer(target);
if(!fb) {
errorShim(GL_INVALID_ENUM);
return;
}
if( !(attachment>=GL_COLOR_ATTACHMENT0 && attachment<(GL_COLOR_ATTACHMENT0+hardext.maxcolorattach))
&& attachment!=GL_DEPTH_ATTACHMENT
&& attachment!=GL_STENCIL_ATTACHMENT
&& attachment!=GL_DEPTH_STENCIL_ATTACHMENT) {
errorShim(GL_INVALID_ENUM);
return;
}
// get renderbuffer
glrenderbuffer_t *rend = find_renderbuffer(renderbuffer);
if(!rend /*|| !rend->renderbuffer*/) {
errorShim(GL_INVALID_OPERATION);
return;
}
if (attachment >= GL_COLOR_ATTACHMENT0 && (attachment < (GL_COLOR_ATTACHMENT0+hardext.maxcolorattach)) && globals4es.fboforcetex) {
if(rend->renderbuffer) {
// drop the renderbuffer attachement and create a texture instead...
int oldactive = glstate->texture.active;
if(oldactive) gl4es_glActiveTexture(GL_TEXTURE0);
gltexture_t *bound = glstate->texture.bound[0][ENABLED_TEX2D];
GLuint oldtex = bound->glname;
// get size of renderbuffer
GLenum format = rend->format;
GLint width = rend->width;
GLint height = rend->height;
// create a texture if needed
if(!rend->secondarytexture) {
GLuint newtex;
gl4es_glGenTextures(1, &newtex);
gl4es_glBindTexture(GL_TEXTURE_2D, newtex);
gl4es_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
gl4es_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
gl4es_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl4es_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl4es_glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
gl4es_glBindTexture(GL_TEXTURE_2D, oldtex);
rend->secondarytexture = newtex;
}
gl4es_glFramebufferTexture2D(target, attachment, GL_TEXTURE_2D, rend->secondarytexture, 0);
// end of cleanup
if(oldactive) gl4es_glActiveTexture(GL_TEXTURE0+oldactive);
} else {
// renderbuffer is 0, unbind the texture attachement...
gl4es_glFramebufferTexture2D(target, attachment, GL_TEXTURE_2D, 0, 0);
}
return;
}
if (attachment==GL_DEPTH_STENCIL_ATTACHMENT) {
// doesn't seems to be supported "as-is" on GLES on PVR
gl4es_glFramebufferRenderbuffer(target, GL_DEPTH_ATTACHMENT, renderbuffertarget, renderbuffer);
gl4es_glFramebufferRenderbuffer(target, GL_STENCIL_ATTACHMENT, renderbuffertarget, renderbuffer);
return;
}
//TODO: handle target=READBUFFER or DRAWBUFFER...
if (attachment==GL_STENCIL_ATTACHMENT) {
if(rend && rend->secondarybuffer)
renderbuffer = rend->secondarybuffer;
}
fb->width = rend->width;
fb->height = rend->height;
if ((GetAttachmentType(fb, attachment) == GL_RENDERBUFFER) && (GetAttachment(fb, attachment)==renderbuffer))
{
// no need to reattach
noerrorShim();
return;
}
SetAttachment(fb, attachment, GL_RENDERBUFFER, renderbuffer, 0);
/*if ((glstate->fbo.current_fb!=0) && (renderbuffer==0)) {
//Hack, avoid unbind a renderbuffer on a framebuffer...
// TODO, avoid binding an already binded RB
noerrorShim();
return;
}*/ // Let it do it now
GLenum ntarget = ReadDraw_Push(target);
errorGL();
gles_glFramebufferRenderbuffer(ntarget, attachment, renderbuffertarget, renderbuffer);
DBG(CheckGLError(1);)
ReadDraw_Pop(target);
}
void APIENTRY_GL4ES gl4es_glDeleteRenderbuffers(GLsizei n, GLuint *renderbuffers) {
DBG(printf("glDeleteRenderbuffer(%d, %p)\n", n, renderbuffers);)
LOAD_GLES2_OR_OES(glDeleteRenderbuffers);
// check if we delete a depthstencil
if (glstate->fbo.renderbufferlist)
for (int i=0; i<n; i++) {
khint_t k;
glrenderbuffer_t *rend;
for (int i = 0; i < n; i++) {
GLuint t = renderbuffers[i];
if(t) {
k = kh_get(renderbufferlist_t, glstate->fbo.renderbufferlist, t);
if (k != kh_end(glstate->fbo.renderbufferlist)) {
rend = kh_value(glstate->fbo.renderbufferlist, k);
if(glstate->fbo.current_rb == rend)
glstate->fbo.current_rb = glstate->fbo.default_rb;
if(rend->secondarybuffer)
gles_glDeleteRenderbuffers(1, &rend->secondarybuffer);
if(rend->secondarytexture)
gl4es_glDeleteTextures(1, &rend->secondarytexture);
free(rend);
kh_del(renderbufferlist_t, glstate->fbo.renderbufferlist, k);
}
}
}
}
errorGL();
gles_glDeleteRenderbuffers(n, renderbuffers);
}
void APIENTRY_GL4ES gl4es_glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) {
DBG(printf("glRenderbufferStorage(%s, %s, %i, %i)\n", PrintEnum(target), PrintEnum(internalformat), width, height);)
LOAD_GLES2_OR_OES(glRenderbufferStorage);
LOAD_GLES2_OR_OES(glGenRenderbuffers);
LOAD_GLES2_OR_OES(glBindRenderbuffer);
glrenderbuffer_t *rend = glstate->fbo.current_rb;
if(!rend->renderbuffer) {
errorShim(GL_INVALID_OPERATION);
return;
}
errorGL();
width = (hardext.npot>0 && !globals4es.potframebuffer)?width:npot(width);
height = (hardext.npot>0 && !globals4es.potframebuffer)?height:npot(height);
int use_secondarybuffer = 0;
int use_secondarytexture = 0;
GLenum format = internalformat;
// check if internal format is GL_DEPTH_STENCIL_EXT
if (internalformat == GL_DEPTH_STENCIL)
internalformat = GL_DEPTH24_STENCIL8;
// in that case, create first a STENCIL one then a DEPTH one....
if ((internalformat == GL_DEPTH24_STENCIL8 && (hardext.depthstencil==0 || ((hardext.vendor&VEND_IMGTEC)==VEND_IMGTEC)))) {
internalformat = (hardext.depth24)?GL_DEPTH_COMPONENT24:GL_DEPTH_COMPONENT16;
// create a stencil buffer if needed
if(!rend->secondarybuffer) {
gles_glGenRenderbuffers(1, &rend->secondarybuffer);
}
use_secondarybuffer = 1;
}
else if (internalformat == GL_DEPTH_COMPONENT || internalformat == GL_DEPTH_COMPONENT32) // Not much is supported on GLES...
internalformat = GL_DEPTH_COMPONENT16;
else if (internalformat == GL_RGB8 && hardext.rgba8==0)
internalformat = GL_RGB565_OES;
else if (internalformat == GL_RGBA8 && hardext.rgba8==0)
internalformat = GL_RGBA4_OES;
else if (internalformat == GL_RGB5)
internalformat = GL_RGB565_OES;
else if (internalformat == GL_R3_G3_B2)
internalformat = GL_RGB565_OES;
else if (internalformat == GL_RGB4)
internalformat = GL_RGBA4_OES;
else if (internalformat == GL_RGBA) {
if(hardext.rgba8==0)
internalformat = GL_RGBA8;
else
internalformat = GL_RGBA4_OES;
}
if(rend->secondarybuffer) {
if(use_secondarybuffer) {
GLuint current_rb = glstate->fbo.current_rb->renderbuffer;
gles_glBindRenderbuffer(GL_RENDERBUFFER, rend->secondarybuffer);
gles_glRenderbufferStorage(target, GL_STENCIL_INDEX8, width, height);
gles_glBindRenderbuffer(GL_RENDERBUFFER, current_rb);
} else {
LOAD_GLES2_OR_OES(glDeleteRenderbuffers);
gles_glDeleteRenderbuffers(1, &rend->secondarybuffer);
rend->secondarybuffer = 0;
}
}
if(rend->secondarytexture) {
// should check if texture is still needed?
gltexture_t *tex = gl4es_getTexture(GL_TEXTURE_2D, rend->secondarytexture);
LOAD_GLES(glActiveTexture);
LOAD_GLES(glBindTexture);
LOAD_GLES(glTexImage2D);
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!=rend->secondarytexture) gles_glBindTexture(GL_TEXTURE_2D, rend->secondarytexture);
tex->nwidth = tex->width = width;
tex->nheight = tex->height = height;
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);
}
rend->width = width;
rend->height = height;
rend->format = format;
rend->actual = internalformat;
gles_glRenderbufferStorage(target, internalformat, width, height);
DBG(CheckGLError(1);)
}
void APIENTRY_GL4ES gl4es_glRenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) { //STUB
gl4es_glRenderbufferStorage(target, internalformat, width, height);
}
void APIENTRY_GL4ES gl4es_glBindRenderbuffer(GLenum target, GLuint renderbuffer) {
DBG(printf("glBindRenderbuffer(%s, %u), binded Fbo=%u\n", PrintEnum(target), renderbuffer, glstate->fbo.current_fb->id);)
LOAD_GLES2_OR_OES(glBindRenderbuffer);
GLuint current = glstate->fbo.current_rb->renderbuffer;
if(current==renderbuffer) {
noerrorShim();
return;
}
glrenderbuffer_t * rend = find_renderbuffer(renderbuffer);
if(!rend || !rend->renderbuffer) {
errorShim(GL_INVALID_OPERATION);
return;
}
glstate->fbo.current_rb = rend;
errorGL();
gles_glBindRenderbuffer(target, renderbuffer);
}
GLboolean APIENTRY_GL4ES gl4es_glIsRenderbuffer(GLuint renderbuffer) {
DBG(printf("glIsRenderbuffer(%u)\n", renderbuffer);)
noerrorShim();
return((find_renderbuffer(renderbuffer)!=NULL)?GL_TRUE:GL_FALSE);
}
void APIENTRY_GL4ES gl4es_glGenerateMipmap(GLenum target) {
DBG(printf("glGenerateMipmap(%s)\n", PrintEnum(target));)
LOAD_GLES2_OR_OES(glGenerateMipmap);
const GLuint rtarget = map_tex_target(target);
realize_bound(glstate->texture.active, target);
gltexture_t *bound = gl4es_getCurrentTexture(target);
if(globals4es.forcenpot && hardext.npot==1) {
if(bound->npot) {
noerrorShim();
return; // no need to generate mipmap, mipmap is disabled here
}
}
errorGL();
if(globals4es.automipmap != 3) {
gles_glGenerateMipmap(rtarget);
bound->mipmap_auto = 1;
/*if(bound->sampler.min_filer != bound->actual.min_filter) // mainly for S3TC textures...
gl4es_glTexParameteri(target, GL_TEXTURE_MIN_FILTER, bound->sampler.min_filer);*/
}
}
void APIENTRY_GL4ES gl4es_glGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint *params) {
DBG(printf("glGetFramebufferAttachmentParameteriv(%s, %s, %s, %p)\n", PrintEnum(target), PrintEnum(attachment), PrintEnum(pname), params);)
LOAD_GLES2_OR_OES(glGetFramebufferAttachmentParameteriv);
glframebuffer_t *fb = get_framebuffer(target);
if(!fb) {
errorShim(GL_INVALID_ENUM);
return;
}
if( !(attachment>=GL_COLOR_ATTACHMENT0 && attachment<(GL_COLOR_ATTACHMENT0+hardext.maxcolorattach))
&& attachment!=GL_DEPTH_ATTACHMENT
&& attachment!=GL_STENCIL_ATTACHMENT
&& attachment!=GL_DEPTH_STENCIL_ATTACHMENT) {
errorShim(GL_INVALID_ENUM);
return;
}
if(pname==GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME) {
noerrorShim();
*params = GetAttachment(fb, attachment);
return;
}
if(pname==GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE) {
noerrorShim();
*params = GetAttachmentType(fb, attachment);
if(*params!=0 && *params!=GL_RENDERBUFFER)
*params = GL_TEXTURE;
return;
}
if(pname==GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL) {
GLenum tmp = GetAttachmentType(fb, attachment);
if(tmp!=0 && tmp!=GL_RENDERBUFFER) {
noerrorShim();
*params = GetAttachmentLevel(fb, attachment);
} else {
errorShim(GL_INVALID_ENUM);
}
return;
}
if(pname==GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE) {
GLenum tmp = GetAttachmentType(fb, attachment);
if(tmp!=0 && tmp!=GL_RENDERBUFFER) {
noerrorShim();
*params = (tmp>=GL_TEXTURE_CUBE_MAP_POSITIVE_X && tmp<=GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)?tmp:0;
} else {
errorShim(GL_INVALID_ENUM);
}
return;
}
// more stuff can be done
/*
if(pname==GL_FRAMEBUFFER_ATTACHMENT_LAYERED) {
GLenum tmp = GetAttachmentType(fb, attachment);
noerrorShim();
*params = (tmp==GL_TEXTURE_CUBE_MAP || tmp==GL_TEXTURE_3D)?GL_TRUE:GL_FALSE;
return;
}
*/
// hack to return DEPTH size
if(attachment==GL_DEPTH_ATTACHMENT && pname==GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE) {
if(hardext.depthtex==0) {
errorGL();
*params = GetAttachment(fb, attachment);
//TODO: Check all this?!
if (*params)
*params = 16; //Depth buffer is 16 on GLES. No check for 24 bits here...
return;
}
// check if it's depth/stencil 16/8, in that case, lie by spoofing 24/8 or some FNA game will fail
int depth, stencil;
GLenum ntarget = ReadDraw_Push(target);
gles_glGetFramebufferAttachmentParameteriv(ntarget, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, &stencil);
gles_glGetFramebufferAttachmentParameteriv(ntarget, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, &depth);
errorGL();
ReadDraw_Pop(target);
if(depth==16 && stencil==8)
depth = 24;
*params = depth;
return;
}
GLenum ntarget = ReadDraw_Push(target);
errorGL();
gles_glGetFramebufferAttachmentParameteriv(ntarget, attachment, pname, params);
ReadDraw_Pop(target);
}
void APIENTRY_GL4ES gl4es_glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint * params) {
DBG(printf("glGetRenderbufferParameteriv(%s, %s, %p)\n", PrintEnum(target), PrintEnum(pname), params);)
LOAD_GLES2_OR_OES(glGetRenderbufferParameteriv);
errorGL();
gles_glGetRenderbufferParameteriv(target, pname, params);
}
void createMainFBO(int width, int height) {
LOAD_GLES2_OR_OES(glGenFramebuffers);
LOAD_GLES2_OR_OES(glBindFramebuffer);
LOAD_GLES2_OR_OES(glFramebufferTexture2D);
LOAD_GLES2_OR_OES(glCheckFramebufferStatus);
LOAD_GLES2_OR_OES(glFramebufferRenderbuffer);
LOAD_GLES2_OR_OES(glRenderbufferStorage);
LOAD_GLES2_OR_OES(glGenRenderbuffers);
LOAD_GLES2_OR_OES(glBindRenderbuffer);
LOAD_GLES(glTexImage2D);
LOAD_GLES(glGenTextures);
LOAD_GLES(glBindTexture);
LOAD_GLES(glActiveTexture);
LOAD_GLES(glTexParameteri);
LOAD_GLES2(glClientActiveTexture);
LOAD_GLES(glClear);
// If there is already a Framebuffer created, let's delete it.... unless it's already the right size!
int createIt = 1;
if (glstate->fbo.mainfbo_fbo) {
if (width==glstate->fbo.mainfbo_width && height==glstate->fbo.mainfbo_height)
return;
//lets adjust the FBO instead of adjusting it
createIt = 0;
}
DBG(printf("LIBGL: Create FBO of %ix%i 32bits\n", width, height);)
// switch to texture unit 0 if needed
if (glstate->texture.active != 0)
gles_glActiveTexture(GL_TEXTURE0);
if (glstate->texture.client != 0 && gles_glClientActiveTexture)
gles_glClientActiveTexture(GL_TEXTURE0);
glstate->fbo.mainfbo_width = width;
glstate->fbo.mainfbo_height = height;
glstate->fbo.mainfbo_nwidth = width = hardext.npot>0?width:npot(width);
glstate->fbo.mainfbo_nheight = height = hardext.npot>0?height:npot(height);
// create the texture
if(createIt)
gles_glGenTextures(1, &glstate->fbo.mainfbo_tex);
gles_glBindTexture(GL_TEXTURE_2D, glstate->fbo.mainfbo_tex);
if(createIt) {
gles_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
gles_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
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);
}
gles_glTexImage2D(GL_TEXTURE_2D, 0, globals4es.fbo_noalpha?GL_RGB:GL_RGBA, width, height,
0, globals4es.fbo_noalpha?GL_RGB:GL_RGBA, GL_UNSIGNED_BYTE, NULL);
gles_glBindTexture(GL_TEXTURE_2D, 0);
// create the render buffers
if(createIt) {
gles_glGenRenderbuffers(1, &glstate->fbo.mainfbo_dep);
gles_glGenRenderbuffers(1, &glstate->fbo.mainfbo_ste);
}
gles_glBindRenderbuffer(GL_RENDERBUFFER, glstate->fbo.mainfbo_ste);
gles_glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height);
gles_glBindRenderbuffer(GL_RENDERBUFFER, glstate->fbo.mainfbo_dep);
gles_glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
gles_glBindRenderbuffer(GL_RENDERBUFFER, 0);
// create a fbo
if(createIt)
gles_glGenFramebuffers(1, &glstate->fbo.mainfbo_fbo);
gles_glBindFramebuffer(GL_FRAMEBUFFER, glstate->fbo.mainfbo_fbo);
// re-attach, even if not creating the fbo...
gles_glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, glstate->fbo.mainfbo_ste);
gles_glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, glstate->fbo.mainfbo_dep);
gles_glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glstate->fbo.mainfbo_tex, 0);
GLenum status = gles_glCheckFramebufferStatus(GL_FRAMEBUFFER);
gles_glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Put everything back
gles_glBindTexture(GL_TEXTURE_2D, glstate->texture.bound[0][ENABLED_TEX2D]->glname);
if (glstate->texture.active != 0)
gles_glActiveTexture(GL_TEXTURE0 + glstate->texture.active);
if (glstate->texture.client != 0 && gles_glClientActiveTexture)
gles_glClientActiveTexture(GL_TEXTURE0 + glstate->texture.client);
GLuint current_rb = glstate->fbo.current_rb->renderbuffer;
gles_glBindRenderbuffer(GL_RENDERBUFFER, current_rb);
// Final check, and bind the fbo for future use
if (status != GL_FRAMEBUFFER_COMPLETE) {
printf("LIBGL: Error while creating main fbo (0x%04X)\n", status);
deleteMainFBO(glstate);
gles_glBindFramebuffer(GL_FRAMEBUFFER, glstate->fbo.current_fb->id);
} else {
gles_glBindFramebuffer(GL_FRAMEBUFFER, (glstate->fbo.current_fb->id)?glstate->fbo.current_fb->id:glstate->fbo.mainfbo_fbo);
// clear color, depth and stencil...
if (glstate->fbo.current_fb->id==0)
gles_glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
}
void blitMainFBO(int x, int y, int width, int height) {
if (glstate->fbo.mainfbo_fbo==0)
return;
// blit the texture
if(!width && !height) {
gl4es_glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
gl4es_glClear(GL_COLOR_BUFFER_BIT);
}
GLint vp[4];
memcpy(vp, &glstate->raster.viewport, sizeof(vp));
gl4es_glViewport(0, 0, glstate->fbowidth, glstate->fboheight);
float rx, ry;
if(!width && !height) {
width = glstate->fbo.mainfbo_width;
height = glstate->fbo.mainfbo_height;
rx = ry = 1.0f;
} else {
y = glstate->fboheight - (y+height);
//y = - y;
rx = (float)width/glstate->fbo.mainfbo_width;
ry = (float)height/glstate->fbo.mainfbo_height;
}
gl4es_blitTexture(glstate->fbo.mainfbo_tex, 0.f, 0.f,
glstate->fbo.mainfbo_width, glstate->fbo.mainfbo_height,
glstate->fbo.mainfbo_nwidth, glstate->fbo.mainfbo_nheight,
rx, ry,
0, 0, x, y, BLIT_OPAQUE);
gl4es_glViewport(vp[0], vp[1], vp[2], vp[3]);
}
void bindMainFBO() {
LOAD_GLES2_OR_OES(glBindFramebuffer);
LOAD_GLES2_OR_OES(glCheckFramebufferStatus);
if (!glstate->fbo.mainfbo_fbo)
return;
if (glstate->fbo.current_fb->id==0) {
gles_glBindFramebuffer(GL_FRAMEBUFFER, glstate->fbo.mainfbo_fbo);
//gles_glCheckFramebufferStatus(GL_FRAMEBUFFER);
}
}
void unbindMainFBO() {
LOAD_GLES2_OR_OES(glBindFramebuffer);
if (!glstate->fbo.mainfbo_fbo)
return;
if (glstate->fbo.current_fb->id==0) {
gles_glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
}
void deleteMainFBO(void *state) {
LOAD_GLES2_OR_OES(glDeleteFramebuffers);
LOAD_GLES2_OR_OES(glDeleteRenderbuffers);
LOAD_GLES(glDeleteTextures);
glstate_t *glstate = (glstate_t*)state;
if (glstate->fbo.mainfbo_dep) {
gles_glDeleteRenderbuffers(1, &glstate->fbo.mainfbo_dep);
glstate->fbo.mainfbo_dep = 0;
}
if (glstate->fbo.mainfbo_ste) {
gles_glDeleteRenderbuffers(1, &glstate->fbo.mainfbo_ste);
glstate->fbo.mainfbo_ste = 0;
}
if (glstate->fbo.mainfbo_tex) {
gles_glDeleteTextures(1, &glstate->fbo.mainfbo_tex);
glstate->fbo.mainfbo_tex = 0;
}
if (glstate->fbo.mainfbo_fbo) {
gles_glDeleteFramebuffers(1, &glstate->fbo.mainfbo_fbo);
glstate->fbo.mainfbo_fbo = 0;
}
// all done...
}
void APIENTRY_GL4ES gl4es_glFramebufferTextureLayer( GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) {
gl4es_glFramebufferTexture2D(target, attachment, GL_TEXTURE_2D, texture, level); // Force Texture2D, ignore layer (should track?)...
}
#ifndef NOX11
void gl4es_SwapBuffers_currentContext(); // defined in glx/glx.c
#endif
void APIENTRY_GL4ES gl4es_glBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) {
// mask will be ignored
// filter will be taken only for ReadFBO has no Texture attached (so readpixel is used)
DBG(printf("glBlitFramebuffer(%d, %d, %d, %d, %d, %d, %d, %d, 0x%04X, %s) fbo_read=%d, fbo_draw=%d\n",
srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, PrintEnum(filter), glstate->fbo.fbo_read->id, glstate->fbo.fbo_draw->id);)
if((mask&GL_COLOR_BUFFER_BIT)==0)
return; // cannot copy DEPTH or STENCIL data on GLES, only COLOR_BUFFER...
if(glstate->fbo.fbo_read == glstate->fbo.fbo_draw && srcX0==dstX0 && srcX1==dstX1 && srcY0==dstY0 && srcY1==dstY1)
return; // no need to try copying on itself
if(dstX1==dstX0 || dstY1==dstY0)
return; // nothing to draw
if(srcX1==srcX0 || srcY1==srcY0)
return; // nothing to draw
GLuint texture = (glstate->fbo.fbo_read->id==0 && glstate->fbo.mainfbo_fbo)?glstate->fbo.mainfbo_tex:glstate->fbo.fbo_read->color[0];
int created = (texture==0 || (glstate->fbo.fbo_read==glstate->fbo.fbo_draw));
int oldtex = glstate->texture.active;
DBG(printf(" blit: created=%d, texture=%u, oldtex=%d\n", created, texture, oldtex);)
if (oldtex)
gl4es_glActiveTexture(GL_TEXTURE0);
float nwidth, nheight;
if (created) {
gltexture_t *old = glstate->texture.bound[ENABLED_TEX2D][0];
gl4es_glGenTextures(1, &texture);
gl4es_glBindTexture(GL_TEXTURE_2D, texture);
gl4es_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
gl4es_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
gl4es_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (filter==GL_LINEAR)?GL_LINEAR:GL_NEAREST);
gl4es_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (filter==GL_LINEAR)?GL_LINEAR:GL_NEAREST);
gl4es_glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, srcX0, srcY0, srcX1-srcX0, srcY1-srcY0, 0);
srcX1-=srcX0; srcX0=0.f;
srcY1-=srcY0; srcY0=0.f;
gl4es_glBindTexture(GL_TEXTURE_2D, old->texture);
}
GLenum glname = texture;
if(texture==glstate->fbo.mainfbo_tex) {
nwidth = glstate->fbo.mainfbo_nwidth;
nheight = glstate->fbo.mainfbo_nheight;
} else {
gltexture_t *tex = gl4es_getTexture(GL_TEXTURE_2D, texture);
if(tex) {
nwidth = tex->nwidth;
nheight = tex->nheight;
glname = tex->glname;
if(!created) {
if((tex->actual.min_filter!=filter) || (tex->actual.mag_filter!=filter)) {
gltexture_t *old = glstate->texture.bound[ENABLED_TEX2D][0];
if(old->texture != glname)
gl4es_glBindTexture(GL_TEXTURE_2D, glname);
gl4es_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
gl4es_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
tex->actual.min_filter = tex->actual.mag_filter = filter;
if(old->texture != glname)
gl4es_glBindTexture(GL_TEXTURE_2D, old->texture);
}
}
} else {
// not good if here!
nwidth = srcX1;
nheight = srcY1;
}
}
float srcW = srcX1-srcX0;
float srcH = srcY1-srcY0;
float zoomx = ((float)(dstX1-dstX0))/srcW;
float zoomy = ((float)(dstY1-dstY0))/srcH;
// get the width / height of write FBO
int fbowidth = 0, fboheight = 0;
int blitfullscreen = 0;
if(glstate->fbo.fbo_draw->id==0/* && glstate->fbo.mainfbo_fbo*/) {
if(globals4es.blitfb0/* || (globals4es.usefb && !globals4es.usefbo)*/)
blitfullscreen = 1;
else {
fbowidth = glstate->fbo.mainfbo_width;
fboheight = glstate->fbo.mainfbo_height;
if((glstate->fbo.mainfbo_width==abs(dstX1-dstX0)) && (glstate->fbo.mainfbo_height==abs(dstY1-dstY0))) {
blitfullscreen = 1;
} else {
if (gl4es_getMainFBSize) {
gl4es_getMainFBSize(&glstate->fbo.mainfbo_width, &glstate->fbo.mainfbo_height);
if((glstate->fbo.mainfbo_width==abs(dstX1-dstX0)) && (glstate->fbo.mainfbo_height==abs(dstY1-dstY0)))
blitfullscreen = 1;
}
}
}
} else {
fbowidth = glstate->fbo.fbo_draw->width;
fboheight = glstate->fbo.fbo_draw->height;
}
GLint vp[4];
memcpy(vp, &glstate->raster.viewport, sizeof(vp));
gl4es_glViewport(0, 0, fbowidth, fboheight);
gl4es_blitTexture(glname, srcX0, srcY0, srcW, srcH, nwidth, nheight, zoomx, zoomy, 0, 0, dstX0, dstY0, BLIT_OPAQUE);
gl4es_glViewport(vp[0], vp[1], vp[2], vp[3]);
if(created) {
gl4es_glDeleteTextures(1, &texture);
}
if(oldtex)
gl4es_glActiveTexture(GL_TEXTURE0+oldtex);
#ifndef NOX11
if(blitfullscreen) // hack, force a swapbuffer (help wine d3d show stuff on certain games)
gl4es_SwapBuffers_currentContext();
#endif
}
GLuint gl4es_getCurrentFBO() {
return (glstate->fbo.current_fb->id)?glstate->fbo.current_fb->id:glstate->fbo.mainfbo_fbo;
}
void gl4es_setCurrentFBO() {
LOAD_GLES2_OR_OES(glBindFramebuffer);
gles_glBindFramebuffer(GL_FRAMEBUFFER, (glstate->fbo.current_fb->id)?glstate->fbo.current_fb->id:glstate->fbo.mainfbo_fbo);
}
// DrawBuffers functions are faked unless GL_EXT_draw_buffers is supported
void APIENTRY_GL4ES gl4es_glDrawBuffers(GLsizei n, const GLenum *bufs) {
DBG(printf("glDrawBuffers(%d, %p) [0]=%s\n", n, bufs, n?PrintEnum(bufs[0]):"nil");)
if(hardext.drawbuffers) {
LOAD_GLES_EXT(glDrawBuffers);
gles_glDrawBuffers(n, bufs);
errorGL();
} else {
if(n<0 || n>hardext.maxdrawbuffers) {
errorShim(GL_INVALID_VALUE);
return;
}
}
glstate->fbo.fbo_draw->n_draw = n;
memcpy(glstate->fbo.fbo_draw->drawbuff, bufs, n*sizeof(GLenum));
noerrorShim();
}
void APIENTRY_GL4ES gl4es_glNamedFramebufferDrawBuffers(GLuint framebuffer, GLsizei n, const GLenum *bufs) {
if(n<0 || n>hardext.maxdrawbuffers) {
errorShim(GL_INVALID_VALUE);
return;
}
glframebuffer_t* fb = find_framebuffer(framebuffer);
if(hardext.drawbuffers) {
GLuint oldf = glstate->fbo.fbo_draw->id;
gl4es_glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb->id);
LOAD_GLES_EXT(glDrawBuffers);
gles_glDrawBuffers(n, bufs);
errorGL();
gl4es_glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oldf);
}
fb->n_draw = n;
memcpy(fb->drawbuff, bufs, n*sizeof(GLenum));
noerrorShim();
}
void APIENTRY_GL4ES gl4es_glClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint * value) {
noerrorShim();
GLenum attch;
switch(buffer) {
case GL_COLOR:
if(drawbuffer>glstate->fbo.fbo_draw->n_draw)
return; // GL_NONE...
attch = glstate->fbo.fbo_draw->drawbuff[buffer];
if(!(attch>=GL_COLOR_ATTACHMENT0 && attch<GL_COLOR_ATTACHMENT0+hardext.maxdrawbuffers)) {
errorShim(GL_INVALID_VALUE);
return;
} else {
GLfloat oldclear[4];
LOAD_GLES_EXT(glDrawBuffers);
// select the buffer...
if(hardext.drawbuffers)
gles_glDrawBuffers(1, (const GLenum *) &drawbuffer);
gl4es_glGetFloatv(GL_COLOR_CLEAR_VALUE, oldclear);
// how to convert the value? Most FB will be 8bits / componant for now...
gl4es_glClearColor(value[0]/127.0f, value[1]/127.0f, value[2]/127.0f, value[3]/127.0f);
gl4es_glClear(GL_COLOR_BUFFER_BIT);
gl4es_glClearColor(oldclear[0], oldclear[1], oldclear[2], oldclear[3]);
// put back the draw buffers...
if(hardext.drawbuffers)
gles_glDrawBuffers(glstate->fbo.fbo_draw->n_draw, glstate->fbo.fbo_draw->drawbuff);
return;
}
break;
case GL_STENCIL:
if(drawbuffer==0) {
GLint old;
gl4es_glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &old);
gl4es_glClearStencil(*value);
gl4es_glClear(GL_STENCIL_BUFFER_BIT);
gl4es_glClearStencil(old);
return;
} else {
errorShim(GL_INVALID_ENUM);
return;
}
default:
errorShim(GL_INVALID_ENUM);
}
return;
}
void APIENTRY_GL4ES gl4es_glClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint * value) {
noerrorShim();
GLenum attch;
switch(buffer) {
case GL_COLOR:
if(drawbuffer>glstate->fbo.fbo_draw->n_draw)
return; // GL_NONE...
attch = glstate->fbo.fbo_draw->drawbuff[buffer];
if(!(attch>=GL_COLOR_ATTACHMENT0 && attch<GL_COLOR_ATTACHMENT0+hardext.maxdrawbuffers)) {
errorShim(GL_INVALID_VALUE);
return;
} else {
GLfloat oldclear[4];
LOAD_GLES_EXT(glDrawBuffers);
// select the buffer...
if(hardext.drawbuffers)
gles_glDrawBuffers(1, (const GLenum *) &drawbuffer);
gl4es_glGetFloatv(GL_COLOR_CLEAR_VALUE, oldclear);
// how to convert the value? Most FB will be 8bits / componant for now...
gl4es_glClearColor(value[0]/255.0f, value[1]/255.0f, value[2]/255.0f, value[3]/255.0f);
gl4es_glClear(GL_COLOR_BUFFER_BIT);
gl4es_glClearColor(oldclear[0], oldclear[1], oldclear[2], oldclear[3]);
// put back the draw buffers...
if(hardext.drawbuffers)
gles_glDrawBuffers(glstate->fbo.fbo_draw->n_draw, glstate->fbo.fbo_draw->drawbuff);
return;
}
break;
default:
errorShim(GL_INVALID_ENUM);
}
return;
}
void APIENTRY_GL4ES gl4es_glClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat * value) {
noerrorShim();
GLenum attch;
switch(buffer) {
case GL_COLOR:
if(drawbuffer>glstate->fbo.fbo_draw->n_draw)
return; // GL_NONE...
attch = glstate->fbo.fbo_draw->drawbuff[buffer];
if(!(attch>=GL_COLOR_ATTACHMENT0 && attch<GL_COLOR_ATTACHMENT0+hardext.maxdrawbuffers)) {
errorShim(GL_INVALID_VALUE);
return;
} else {
GLfloat oldclear[4];
LOAD_GLES_EXT(glDrawBuffers);
// select the buffer...
if(hardext.drawbuffers)
gles_glDrawBuffers(1, (const GLenum *) &drawbuffer);
gl4es_glGetFloatv(GL_COLOR_CLEAR_VALUE, oldclear);
// how to convert the value? Most FB will be 8bits / componant for now...
gl4es_glClearColor(value[0], value[1], value[2], value[3]);
gl4es_glClear(GL_COLOR_BUFFER_BIT);
gl4es_glClearColor(oldclear[0], oldclear[1], oldclear[2], oldclear[3]);
// put back the draw buffers...
if(hardext.drawbuffers)
gles_glDrawBuffers(glstate->fbo.fbo_draw->n_draw, glstate->fbo.fbo_draw->drawbuff);
return;
}
break;
case GL_DEPTH:
if(drawbuffer==0) {
GLint old;
gl4es_glGetIntegerv(GL_DEPTH_CLEAR_VALUE, &old);
gl4es_glClearDepthf(*value);
gl4es_glClear(GL_DEPTH_BUFFER_BIT);
gl4es_glClearDepthf(old);
return;
} else {
errorShim(GL_INVALID_ENUM);
return;
}
default:
errorShim(GL_INVALID_ENUM);
}
return;
}
void APIENTRY_GL4ES gl4es_glClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) {
if(buffer!=GL_DEPTH_STENCIL || drawbuffer!=0) {
errorShim(GL_INVALID_ENUM);
return;
}
GLint olds, oldd;
gl4es_glGetIntegerv(GL_DEPTH_CLEAR_VALUE, &oldd);
gl4es_glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &olds);
gl4es_glClearDepthf(depth);
gl4es_glClearStencil(stencil);
gl4es_glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
gl4es_glClearDepthf(oldd);
gl4es_glClearStencil(olds);
}
void APIENTRY_GL4ES gl4es_glClearNamedFramebufferiv(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint *value) {
GLuint oldf = glstate->fbo.fbo_draw->id;
GLenum target = (glstate->fbo.fbo_draw==glstate->fbo.fbo_read)?GL_FRAMEBUFFER:GL_DRAW_FRAMEBUFFER;
gl4es_glBindFramebuffer(target, framebuffer);
gl4es_glClearBufferiv(buffer, drawbuffer, value);
gl4es_glBindFramebuffer(target, oldf);
}
void APIENTRY_GL4ES gl4es_glClearNamedFramebufferuiv(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint *value) {
GLuint oldf = glstate->fbo.fbo_draw->id;
GLenum target = (glstate->fbo.fbo_draw==glstate->fbo.fbo_read)?GL_FRAMEBUFFER:GL_DRAW_FRAMEBUFFER;
gl4es_glBindFramebuffer(target, framebuffer);
gl4es_glClearBufferuiv(buffer, drawbuffer, value);
gl4es_glBindFramebuffer(target, oldf);
}
void APIENTRY_GL4ES gl4es_glClearNamedFramebufferfv(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat *value) {
GLuint oldf = glstate->fbo.fbo_draw->id;
GLenum target = (glstate->fbo.fbo_draw==glstate->fbo.fbo_read)?GL_FRAMEBUFFER:GL_DRAW_FRAMEBUFFER;
gl4es_glBindFramebuffer(target, framebuffer);
gl4es_glClearBufferfv(buffer, drawbuffer, value);
gl4es_glBindFramebuffer(target, oldf);
}
void APIENTRY_GL4ES gl4es_glClearNamedFramebufferfi(GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) {
GLuint oldf = glstate->fbo.fbo_draw->id;
GLenum target = (glstate->fbo.fbo_draw==glstate->fbo.fbo_read)?GL_FRAMEBUFFER:GL_DRAW_FRAMEBUFFER;
gl4es_glBindFramebuffer(target, framebuffer);
gl4es_glClearBufferfi(buffer, drawbuffer, depth, stencil);
gl4es_glBindFramebuffer(target, oldf);
}
void APIENTRY_GL4ES gl4es_glColorMaskIndexed(GLuint framebuffer, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) {
GLuint oldf = glstate->fbo.fbo_draw->id;
GLenum target = (glstate->fbo.fbo_draw==glstate->fbo.fbo_read)?GL_FRAMEBUFFER:GL_DRAW_FRAMEBUFFER;
gl4es_glBindFramebuffer(target, framebuffer);
gl4es_glColorMask(red, green, blue, alpha);
gl4es_glBindFramebuffer(target, oldf);
}
void gl4es_saveCurrentFBO()
{
// this, in fact, bind FBO to 0 if it wasn't
GLuint framebuffer = (glstate->fbo.current_fb)?glstate->fbo.current_fb->id:0;
if(framebuffer==0)
framebuffer = glstate->fbo.mainfbo_fbo;
if(framebuffer) {
LOAD_GLES2_OR_OES(glBindFramebuffer);
if(hardext.vendor&VEND_ARM)
gl4es_glFinish(); //MALI seems to need a flush commandbefore unbinding the Framebuffer here
gles_glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
}
void gl4es_restoreCurrentFBO()
{
// Bind back current FBO if it wasn't 0
GLuint framebuffer = (glstate->fbo.current_fb)?glstate->fbo.current_fb->id:0;
if(framebuffer==0)
framebuffer = glstate->fbo.mainfbo_fbo;
if(framebuffer) {
LOAD_GLES2_OR_OES(glBindFramebuffer);
gles_glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
}
}
// direct wrapper
AliasExport(void,glGenFramebuffers,,(GLsizei n, GLuint *ids));
AliasExport(void,glDeleteFramebuffers,,(GLsizei n, GLuint *framebuffers));
AliasExport(GLboolean,glIsFramebuffer,,(GLuint framebuffer));
AliasExport(GLenum,glCheckFramebufferStatus,,(GLenum target));
AliasExport(void,glBindFramebuffer,,(GLenum target, GLuint framebuffer));
AliasExport(void,glFramebufferTexture1D,,(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level));
AliasExport(void,glFramebufferTexture2D,,(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level));
AliasExport(void,glFramebufferTexture3D,,(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint layer));
AliasExport(void,glGenRenderbuffers,,(GLsizei n, GLuint *renderbuffers));
AliasExport(void,glFramebufferRenderbuffer,,(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer));
AliasExport(void,glDeleteRenderbuffers,,(GLsizei n, GLuint *renderbuffers));
AliasExport(void,glRenderbufferStorage,,(GLenum target, GLenum internalformat, GLsizei width, GLsizei height));
AliasExport(void,glBindRenderbuffer,,(GLenum target, GLuint renderbuffer));
AliasExport(GLboolean,glIsRenderbuffer,,(GLuint renderbuffer));
AliasExport(void,glGenerateMipmap,,(GLenum target));
AliasExport(void,glGetFramebufferAttachmentParameteriv,,(GLenum target, GLenum attachment, GLenum pname, GLint *params));
AliasExport(void,glGetRenderbufferParameteriv,,(GLenum target, GLenum pname, GLint * params));
AliasExport(void,glFramebufferTextureLayer,,( GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer));
AliasExport(void,glBlitFramebuffer,,(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter));
// EXT direct wrapper
AliasExport(void,glGenFramebuffers,EXT,(GLsizei n, GLuint *ids));
AliasExport(void,glDeleteFramebuffers,EXT,(GLsizei n, GLuint *framebuffers));
AliasExport(GLboolean,glIsFramebuffer,EXT,(GLuint framebuffer));
AliasExport(GLenum,glCheckFramebufferStatus,EXT,(GLenum target));
AliasExport(void,glBindFramebuffer,EXT,(GLenum target, GLuint framebuffer));
AliasExport(void,glFramebufferTexture1D,EXT,(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level));
AliasExport(void,glFramebufferTexture2D,EXT,(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level));
AliasExport(void,glFramebufferTexture3D,EXT,(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint layer));
AliasExport(void,glGenRenderbuffers,EXT,(GLsizei n, GLuint *renderbuffers));
AliasExport(void,glFramebufferRenderbuffer,EXT,(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer));
AliasExport(void,glDeleteRenderbuffers,EXT,(GLsizei n, GLuint *renderbuffers));
AliasExport(void,glRenderbufferStorage,EXT,(GLenum target, GLenum internalformat, GLsizei width, GLsizei height));
AliasExport(void,glBindRenderbuffer,EXT,(GLenum target, GLuint renderbuffer));
AliasExport(GLboolean,glIsRenderbuffer,EXT,(GLuint renderbuffer));
AliasExport(void,glGenerateMipmap,EXT,(GLenum target));
AliasExport(void,glGetFramebufferAttachmentParameteriv,EXT,(GLenum target, GLenum attachment, GLenum pname, GLint *params));
AliasExport(void,glGetRenderbufferParameteriv,EXT,(GLenum target, GLenum pname, GLint * params));
AliasExport(void,glFramebufferTextureLayer,EXT,( GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer));
AliasExport(void,glBlitFramebuffer,EXT,(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter));
// Multisample stub
AliasExport(void,glRenderbufferStorageMultisample,,(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height));
// DrawBuffers
AliasExport(void,glDrawBuffers,,(GLsizei n, const GLenum *bufs));
AliasExport(void,glDrawBuffers,ARB,(GLsizei n, const GLenum *bufs));
AliasExport(void,glNamedFramebufferDrawBuffers,,(GLuint framebuffer, GLsizei n, const GLenum *bufs));
AliasExport(void,glNamedFramebufferDrawBuffers,EXT,(GLuint framebuffer, GLsizei n, const GLenum *bufs));
// ClearBuffer...
AliasExport(void,glClearBufferiv,,(GLenum buffer, GLint drawbuffer, const GLint * value));
AliasExport(void,glClearBufferuiv,,(GLenum buffer, GLint drawbuffer, const GLuint * value));
AliasExport(void,glClearBufferfv,,(GLenum buffer, GLint drawbuffer, const GLfloat * value));
AliasExport(void,glClearBufferfi,,(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil));
AliasExport(void,glClearNamedFramebufferiv,,(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint *value));
AliasExport(void,glClearNamedFramebufferuiv,,(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint *value));
AliasExport(void,glClearNamedFramebufferfv,,(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat *value));
AliasExport(void,glClearNamedFramebufferfi,,(GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil));
AliasExport(void,glClearNamedFramebufferiv,EXT,(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint *value));
AliasExport(void,glClearNamedFramebufferuiv,EXT,(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint *value));
AliasExport(void,glClearNamedFramebufferfv,EXT,(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat *value));
AliasExport(void,glClearNamedFramebufferfi,EXT,(GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil));