Files
LibretroAdmin 3be1f43720 android: build GLideN64 with GLES3=1
GLideN64 builds as its own ndk module with its src/, src/osal/ and src/inc/
directories first on the include path, matching the Makefile's GLideN64
pattern rules; its header names collide with the other video plugins. The
core's C sources take -fcommon as the Makefile does, and GLES3=1 links
libGLESv3.

The bundled glcorearb.h takes GLsizeiptr and GLintptr from khrplatform.h so
it agrees with the GLES 3 headers on 32-bit targets.

gles2n64's VI state is renamed to gln64VI: it is larger than GLideN64's VI
and must not share its storage.
2026-10-09 10:35:45 +02:00

1220 lines
36 KiB
C

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <time.h>
#ifdef _WIN32
#include <direct.h>
#else
#include <unistd.h>
#endif
#include <boolean.h>
#include <retro_miscellaneous.h>
#include "Common.h"
#include "gles2N64.h"
#include "OpenGL.h"
#include "N64.h"
#include "gSP.h"
#include "gDP.h"
#include "Textures.h"
#include "FrameBuffer.h"
#include "ShaderCombiner.h"
#include "VI.h"
#include "RSP.h"
#include "Config.h"
#include "../../Graphics/RDP/gDP_state.h"
#include "../../Graphics/RSP/gSP_state.h"
GLInfo OGL;
static bool m_bFlatColors;
int OGL_IsExtSupported( const char *extension )
{
const GLubyte *extensions = NULL;
const GLubyte *start;
GLubyte *where, *terminator;
where = (GLubyte *) strchr(extension, ' ');
if (where || *extension == '\0')
return 0;
extensions = glGetString(GL_EXTENSIONS);
if (!extensions) return 0;
start = extensions;
for (;;)
{
where = (GLubyte *) strstr((const char *) start, extension);
if (!where)
break;
terminator = where + strlen(extension);
if (where == start || *(where - 1) == ' ')
if (*terminator == ' ' || *terminator == '\0')
return 1;
start = terminator;
}
return 0;
}
static void _initStates(void)
{
glDisable( GL_CULL_FACE );
glEnableVertexAttribArray( SC_POSITION );
glEnable( GL_DEPTH_TEST );
glDepthFunc( GL_ALWAYS );
glDepthMask( GL_FALSE );
glEnable( GL_SCISSOR_TEST );
glDepthRange(0.0f, 1.0f);
glPolygonOffset(-0.2f, -0.2f);
glViewport(0, OGL_GetHeightOffset(), OGL_GetScreenWidth(), OGL_GetScreenHeight());
glClearColor( 0.0f, 0.0f, 0.0f, 0.0f);
glClear( GL_COLOR_BUFFER_BIT);
srand( time(NULL) );
}
void OGL_UpdateScale(void)
{
if (gln64VI.width == 0 || gln64VI.height == 0)
return;
OGL.scaleX = OGL_GetScreenWidth() / (float)gln64VI.width;
OGL.scaleY = OGL_GetScreenHeight() / (float)gln64VI.height;
}
uint32_t OGL_GetScreenWidth(void)
{
return config.screen.width;
}
uint32_t OGL_GetScreenHeight(void)
{
return config.screen.height;
}
float OGL_GetScaleX(void)
{
return OGL.scaleX;
}
float OGL_GetScaleY(void)
{
return OGL.scaleY;
}
uint32_t OGL_GetHeightOffset(void)
{
return OGL.heightOffset;
}
void OGL_Stop(void)
{
LOG(LOG_MINIMAL, "Stopping OpenGL\n");
Combiner_Destroy();
TextureCache_Destroy();
}
static void _updateCullFace(void)
{
if (gSP.geometryMode & G_CULL_BOTH)
{
glEnable( GL_CULL_FACE );
if (gSP.geometryMode & G_CULL_BACK)
glCullFace(GL_BACK);
else
glCullFace(GL_FRONT);
}
else
glDisable(GL_CULL_FACE);
}
/* TODO/FIXME - not complete yet */
static void _updateViewport(void)
{
const uint32_t VI_height = gln64VI.height;
const float scaleX = OGL_GetScaleX();
const float scaleY = OGL_GetScaleY();
float Xf = gSP.viewport.vscale[0] < 0 ? (gSP.viewport.x + gSP.viewport.vscale[0] * 2.0f) : gSP.viewport.x;
const GLint X = (GLint)(Xf * scaleX);
const GLint Y = gSP.viewport.vscale[1] < 0 ? (GLint)((gSP.viewport.y + gSP.viewport.vscale[1] * 2.0f) * scaleY) : (GLint)((VI_height - (gSP.viewport.y + gSP.viewport.height)) * scaleY);
glViewport(X,
Y + OGL_GetHeightOffset(),
MAX((GLint)(gSP.viewport.width * scaleX), 0),
MAX((GLint)(gSP.viewport.height * scaleY), 0));
gSP.changed &= ~CHANGED_VIEWPORT;
}
static void _updateDepthUpdate(void)
{
if (gDP.otherMode.depthUpdate)
glDepthMask(GL_TRUE);
else
glDepthMask(GL_FALSE);
}
static void _updateScissor(struct FrameBuffer *_pBuffer)
{
uint32_t heightOffset, screenHeight;
float scaleX, scaleY;
float SX0 = gDP.scissor.ulx;
float SX1 = gDP.scissor.lrx;
if (_pBuffer == NULL)
{
scaleX = OGL_GetScaleX();
scaleY = OGL_GetScaleY();
heightOffset = OGL_GetHeightOffset();
screenHeight = gln64VI.height;
}
else
{
scaleX = _pBuffer->m_scaleX;
scaleY = _pBuffer->m_scaleY;
heightOffset = 0;
screenHeight = (_pBuffer->m_height == 0) ? gln64VI.height : _pBuffer->m_height;
}
#if 0
if (ogl.isAdjustScreen() && gSP.viewport.width < gDP.colorImage.width && gDP.colorImage.width > gln64VI.width * 98 / 100)
_adjustScissorX(SX0, SX1, ogl.getAdjustScale());
#endif
glScissor(
(GLint)(SX0 * scaleX),
(GLint)((screenHeight - gDP.scissor.lry) * scaleY + heightOffset),
MAX((GLint)((SX1 - gDP.scissor.ulx) * scaleX), 0),
MAX((GLint)((gDP.scissor.lry - gDP.scissor.uly) * scaleY), 0)
);
gDP.changed &= ~CHANGED_SCISSOR;
}
static void _setBlendMode(void)
{
const uint32_t blendmode = gDP.otherMode.l >> 16;
// 0x7000 = CVG_X_ALPHA|ALPHA_CVG_SEL|FORCE_BL
if (gDP.otherMode.alphaCvgSel != 0 && (gDP.otherMode.l & 0x7000) != 0x7000) {
switch (blendmode) {
case 0x4055: // Mario Golf
case 0x5055: // Paper Mario intro clr_mem * a_in + clr_mem * a_mem
glEnable(GL_BLEND);
glBlendFunc(GL_ZERO, GL_ONE);
break;
default:
glDisable(GL_BLEND);
}
return;
}
if (gDP.otherMode.forceBlender != 0 && gDP.otherMode.cycleType < G_CYC_COPY) {
glEnable( GL_BLEND );
switch (blendmode)
{
// Mace objects
case 0x0382:
// Mace special blend mode, see GLSLCombiner.cpp
case 0x0091:
// 1080 Sky
case 0x0C08:
// Used LOTS of places
case 0x0F0A:
//DK64 blue prints
case 0x0302:
// Bomberman 2 special blend mode, see GLSLCombiner.cpp
case 0xA500:
//Sin and Punishment
case 0xCB02:
// Battlezone
// clr_in * a + clr_in * (1-a)
case 0xC800:
// Conker BFD
// clr_in * a_fog + clr_fog * (1-a)
// clr_in * 0 + clr_in * 1
case 0x07C2:
case 0x00C0:
//ISS64
case 0xC302:
// Donald Duck
case 0xC702:
glBlendFunc(GL_ONE, GL_ZERO);
break;
case 0x0F1A:
if (gDP.otherMode.cycleType == G_CYC_1CYCLE)
glBlendFunc(GL_ONE, GL_ZERO);
else
glBlendFunc(GL_ZERO, GL_ONE);
break;
//Space Invaders
case 0x0448: // Add
case 0x055A:
glBlendFunc( GL_ONE, GL_ONE );
break;
case 0xc712: // Pokemon Stadium?
case 0xAF50: // LOT in Zelda: MM
case 0x0F5A: // LOT in Zelda: MM
case 0x0FA5: // Seems to be doing just blend color - maybe combiner can be used for this?
case 0x5055: // Used in Paper Mario intro, I'm not sure if this is right...
//clr_in * 0 + clr_mem * 1
glBlendFunc( GL_ZERO, GL_ONE );
break;
case 0x5F50: //clr_mem * 0 + clr_mem * (1-a)
glBlendFunc( GL_ZERO, GL_ONE_MINUS_SRC_ALPHA );
break;
case 0xF550: //clr_fog * a_fog + clr_mem * (1-a)
case 0x0150: // spiderman
case 0x0550: // bomberman 64
case 0x0D18: //clr_in * a_fog + clr_mem * (1-a)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
break;
case 0xC912: //40 winks, clr_in * a_fog + clr_mem * 1
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
break;
case 0x0040: // Fzero
case 0xC810: // Blends fog
case 0xC811: // Blends fog
case 0x0C18: // Standard interpolated blend
case 0x0C19: // Used for antialiasing
case 0x0050: // Standard interpolated blend
case 0x0051: // Standard interpolated blend
case 0x0055: // Used for antialiasing
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
break;
case 0x5000: // V8 explosions
glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
break;
default:
LOG(LOG_VERBOSE, "Unhandled blend mode=%x", gDP.otherMode.l >> 16);
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
break;
}
}
else if ((config.generalEmulation.hacks & hack_pilotWings) != 0 && (gDP.otherMode.l & 0x80) != 0) { //CLR_ON_CVG without FORCE_BL
glEnable(GL_BLEND);
glBlendFunc(GL_ZERO, GL_ONE);
}
/* TODO/FIXME: update */
else if ((config.generalEmulation.hacks & hack_blastCorps) != 0 && gSP.texture.on == 0 /* && currentCombiner()->usesTex() */)
{
// Blast Corps
glEnable(GL_BLEND);
glBlendFunc(GL_ZERO, GL_ONE);
}
else
{
glDisable( GL_BLEND );
}
}
static void _updateStates(void)
{
if (gDP.otherMode.cycleType == G_CYC_COPY)
Combiner_Set(EncodeCombineMode(0, 0, 0, TEXEL0, 0, 0, 0, TEXEL0, 0, 0, 0, TEXEL0, 0, 0, 0, TEXEL0), -1);
else if (gDP.otherMode.cycleType == G_CYC_FILL)
Combiner_Set(EncodeCombineMode(0, 0, 0, SHADE, 0, 0, 0, 1, 0, 0, 0, SHADE, 0, 0, 0, 1), -1);
else
Combiner_Set(gDP.combine.mux, -1);
if (gSP.changed & CHANGED_GEOMETRYMODE)
{
_updateCullFace();
gSP.changed &= ~CHANGED_GEOMETRYMODE;
}
if (gDP.changed & CHANGED_RENDERMODE || gDP.changed & CHANGED_CYCLETYPE)
{
if (((gSP.geometryMode & G_ZBUFFER) || gDP.otherMode.depthSource == G_ZS_PRIM) && gDP.otherMode.cycleType <= G_CYC_2CYCLE)
{
if (gDP.otherMode.depthCompare != 0)
{
switch (gDP.otherMode.depthMode)
{
case ZMODE_OPA:
glDisable(GL_POLYGON_OFFSET_FILL);
glDepthFunc(GL_LEQUAL);
break;
case ZMODE_INTER:
glDisable(GL_POLYGON_OFFSET_FILL);
glDepthFunc(GL_LEQUAL);
break;
case ZMODE_XLU:
// Max || Infront;
glDisable(GL_POLYGON_OFFSET_FILL);
if (gDP.otherMode.depthSource == G_ZS_PRIM && gDP.primDepth.z == 1.0f)
// Max
glDepthFunc(GL_LEQUAL);
else
// Infront
glDepthFunc(GL_LESS);
break;
case ZMODE_DEC:
glEnable(GL_POLYGON_OFFSET_FILL);
glDepthFunc(GL_LEQUAL);
break;
}
}
else
{
glDisable(GL_POLYGON_OFFSET_FILL);
glDepthFunc(GL_ALWAYS);
}
_updateDepthUpdate();
glEnable(GL_DEPTH_TEST);
#ifdef NEW
if (!GBI.isNoN())
glDisable(GL_DEPTH_CLAMP);
#endif
}
else
{
glDisable(GL_DEPTH_TEST);
#ifdef NEW
if (!GBI.isNoN())
glEnable(GL_DEPTH_CLAMP);
#endif
}
}
if ((gDP.changed & CHANGED_RENDERMODE))
SC_SetUniform1f(uAlphaRef, (gDP.otherMode.cvgXAlpha) ? 0.5f : gDP.blendColor.a);
if (gDP.changed & CHANGED_SCISSOR)
_updateScissor(FrameBuffer_GetCurrent());
if (gSP.changed & CHANGED_VIEWPORT)
_updateViewport();
if (gSP.changed & CHANGED_LIGHT)
ShaderCombiner_UpdateLightParameters();
if (gSP.changed & CHANGED_FOGPOSITION)
{
SC_SetUniform1f(uFogScale, (float) gSP.fog.multiplier / 255.0f);
SC_SetUniform1f(uFogOffset, (float) gSP.fog.offset / 255.0f);
}
if ((gSP.changed & CHANGED_TEXTURE) || (gDP.changed & CHANGED_TILE) || (gDP.changed & CHANGED_TMEM))
{
//For some reason updating the texture cache on the first frame of LOZ:OOT causes a NULL Pointer exception...
if (scProgramCurrent)
{
if (scProgramCurrent->usesT0)
{
TextureCache_Update(0);
SC_ForceUniform2f(uTexOffset[0], gSP.textureTile[0]->fuls, gSP.textureTile[0]->fult);
SC_ForceUniform2f(uCacheShiftScale[0], cache.current[0]->shiftScaleS, cache.current[0]->shiftScaleT);
SC_ForceUniform2f(uCacheScale[0], cache.current[0]->scaleS, cache.current[0]->scaleT);
SC_ForceUniform2f(uCacheOffset[0], cache.current[0]->offsetS, cache.current[0]->offsetT);
}
//else TextureCache_ActivateDummy(0);
//Note: enabling dummies makes some F-zero X textures flicker.... strange.
if (scProgramCurrent->usesT1)
{
TextureCache_Update(1);
SC_ForceUniform2f(uTexOffset[1], gSP.textureTile[1]->fuls, gSP.textureTile[1]->fult);
SC_ForceUniform2f(uCacheShiftScale[1], cache.current[1]->shiftScaleS, cache.current[1]->shiftScaleT);
SC_ForceUniform2f(uCacheScale[1], cache.current[1]->scaleS, cache.current[1]->scaleT);
SC_ForceUniform2f(uCacheOffset[1], cache.current[1]->offsetS, cache.current[1]->offsetT);
}
//else TextureCache_ActivateDummy(1);
}
}
if ((gDP.changed & (CHANGED_RENDERMODE | CHANGED_CYCLETYPE)))
{
_setBlendMode();
gDP.changed &= ~(CHANGED_RENDERMODE | CHANGED_CYCLETYPE);
}
gDP.changed &= CHANGED_TILE | CHANGED_TMEM;
gSP.changed &= CHANGED_TEXTURE | CHANGED_MATRIX;
}
void OGL_DrawTriangle(struct SPVertex *vertices, int v0, int v1, int v2)
{
}
static void OGL_SetColorArray(void)
{
if (scProgramCurrent->usesCol || gDP.otherMode.c1_m1b == 2)
glEnableVertexAttribArray(SC_COLOR);
else
glDisableVertexAttribArray(SC_COLOR);
}
static void OGL_SetTexCoordArrays(void)
{
if (OGL.renderState == RS_TRIANGLE)
{
glDisableVertexAttribArray(SC_TEXCOORD1);
if (scProgramCurrent->usesT0 || scProgramCurrent->usesT1)
glEnableVertexAttribArray(SC_TEXCOORD0);
else
glDisableVertexAttribArray(SC_TEXCOORD0);
}
else
{
if (scProgramCurrent->usesT0)
glEnableVertexAttribArray(SC_TEXCOORD0);
else
glDisableVertexAttribArray(SC_TEXCOORD0);
if (scProgramCurrent->usesT1)
glEnableVertexAttribArray(SC_TEXCOORD1);
else
glDisableVertexAttribArray(SC_TEXCOORD1);
}
}
static void OGL_prepareDrawTriangle(bool _dma)
{
bool updateColorArrays, updateArrays;
if (gSP.changed || gDP.changed)
_updateStates();
updateArrays = OGL.renderState != RS_TRIANGLE;
if (updateArrays || scProgramChanged)
{
OGL.renderState = RS_TRIANGLE;
OGL_SetColorArray();
OGL_SetTexCoordArrays();
glDisableVertexAttribArray(SC_TEXCOORD1);
SC_ForceUniform1f(uRenderState, RS_TRIANGLE);
}
updateColorArrays = m_bFlatColors != (!__RSP.bLLE && (gSP.geometryMode & G_SHADING_SMOOTH) == 0);
if (updateColorArrays)
m_bFlatColors = !m_bFlatColors;
if (updateArrays)
{
struct SPVertex *pVtx = (struct SPVertex*)&OGL.triangles.vertices[0];
glVertexAttribPointer(SC_POSITION, 4, GL_FLOAT, GL_FALSE, sizeof(struct SPVertex), &pVtx->x);
if (m_bFlatColors)
glVertexAttribPointer(SC_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(struct SPVertex), &pVtx->flat_r);
else
glVertexAttribPointer(SC_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(struct SPVertex), &pVtx->r);
glVertexAttribPointer(SC_TEXCOORD0, 2, GL_FLOAT, GL_FALSE, sizeof(struct SPVertex), &pVtx->s);
}
else if (updateColorArrays)
{
struct SPVertex *pVtx = (struct SPVertex*)&OGL.triangles.vertices[0];
if (m_bFlatColors)
glVertexAttribPointer(SC_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(struct SPVertex), &pVtx->flat_r);
else
glVertexAttribPointer(SC_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(struct SPVertex), &pVtx->r);
}
}
void OGL_DrawLLETriangle(uint32_t _numVtx)
{
struct FrameBuffer * pCurrentBuffer;
float scaleX, scaleY;
uint32_t i;
if (_numVtx == 0)
return;
gSP.changed &= ~CHANGED_GEOMETRYMODE; // Don't update cull mode
OGL_prepareDrawTriangle(false);
glDisable(GL_CULL_FACE);
pCurrentBuffer = FrameBuffer_GetCurrent();
if (pCurrentBuffer == NULL)
{
glViewport( 0, OGL_GetHeightOffset(), OGL_GetScreenWidth(), OGL_GetScreenHeight());
}
else
glViewport(0, 0, pCurrentBuffer->m_width * pCurrentBuffer->m_scaleX, pCurrentBuffer->m_height * pCurrentBuffer->m_scaleY);
scaleX = pCurrentBuffer != NULL ? 1.0f / pCurrentBuffer->m_width : gln64VI.rwidth;
scaleY = pCurrentBuffer != NULL ? 1.0f / pCurrentBuffer->m_height : gln64VI.rheight;
for (i = 0; i < _numVtx; ++i)
{
struct SPVertex *vtx = (struct SPVertex*)&OGL.triangles.vertices[i];
vtx->HWLight = 0;
vtx->x = vtx->x * (2.0f * scaleX) - 1.0f;
vtx->x *= vtx->w;
vtx->y = vtx->y * (-2.0f * scaleY) + 1.0f;
vtx->y *= vtx->w;
vtx->z *= vtx->w;
if (gDP.otherMode.texturePersp == 0)
{
vtx->s *= 2.0f;
vtx->t *= 2.0f;
}
}
glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVtx);
OGL.triangles.num = 0;
#if 0
frameBufferList().setBufferChanged();
#endif
gSP.changed |= CHANGED_VIEWPORT | CHANGED_GEOMETRYMODE;
}
void OGL_AddTriangle(int v0, int v1, int v2)
{
uint32_t i;
struct SPVertex *vtx = NULL;
OGL.triangles.elements[OGL.triangles.num++] = v0;
OGL.triangles.elements[OGL.triangles.num++] = v1;
OGL.triangles.elements[OGL.triangles.num++] = v2;
if ((gSP.geometryMode & G_SHADE) == 0)
{
/* Prim shading */
for (i = OGL.triangles.num - 3; i < OGL.triangles.num; ++i)
{
vtx = (struct SPVertex*)&OGL.triangles.vertices[OGL.triangles.elements[i]];
vtx->flat_r = gDP.primColor.r;
vtx->flat_g = gDP.primColor.g;
vtx->flat_b = gDP.primColor.b;
vtx->flat_a = gDP.primColor.a;
}
}
else if ((gSP.geometryMode & G_SHADING_SMOOTH) == 0)
{
/* Flat shading */
struct SPVertex *vtx0 = (struct SPVertex*)&OGL.triangles.vertices[v0];
for (i = OGL.triangles.num - 3; i < OGL.triangles.num; ++i)
{
vtx = (struct SPVertex*)&OGL.triangles.vertices[OGL.triangles.elements[i]];
vtx->flat_r = vtx0->r;
vtx->flat_g = vtx0->g;
vtx->flat_b = vtx0->b;
vtx->flat_a = vtx0->a;
}
}
if (gDP.otherMode.depthSource == G_ZS_PRIM)
{
for (i = OGL.triangles.num - 3; i < OGL.triangles.num; ++i)
{
vtx = (struct SPVertex*)&OGL.triangles.vertices[OGL.triangles.elements[i]];
vtx->z = gDP.primDepth.z * vtx->w;
}
}
}
void OGL_DrawDMATriangles(uint32_t _numVtx)
{
if (_numVtx == 0)
return;
OGL_prepareDrawTriangle(true);
glDrawArrays(GL_TRIANGLES, 0, _numVtx);
}
void OGL_DrawTriangles(void)
{
if (OGL.triangles.num == 0)
return;
OGL_prepareDrawTriangle(false);
glDrawElements(GL_TRIANGLES, OGL.triangles.num, GL_UNSIGNED_BYTE, OGL.triangles.elements);
OGL.triangles.num = 0;
}
void OGL_DrawLine(int v0, int v1, float width )
{
unsigned short elem[2];
if (gSP.changed || gDP.changed)
_updateStates();
if (OGL.renderState != RS_LINE || scProgramChanged)
{
OGL_SetColorArray();
glDisableVertexAttribArray(SC_TEXCOORD0);
glDisableVertexAttribArray(SC_TEXCOORD1);
glVertexAttribPointer(SC_POSITION, 4, GL_FLOAT, GL_FALSE, sizeof(struct SPVertex), &OGL.triangles.vertices[0].x);
glVertexAttribPointer(SC_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(struct SPVertex), &OGL.triangles.vertices[0].r);
SC_ForceUniform1f(uRenderState, RS_LINE);
_updateCullFace();
_updateViewport();
OGL.renderState = RS_LINE;
}
elem[0] = v0;
elem[1] = v1;
glLineWidth( width * OGL.scaleX );
glDrawElements(GL_LINES, 2, GL_UNSIGNED_SHORT, elem);
}
void OGL_DrawRect( int ulx, int uly, int lrx, int lry, float *color)
{
struct FrameBuffer *pCurrentBuffer;
float scaleX, scaleY, Z, W;
bool updateArrays;
gSP.changed &= ~CHANGED_GEOMETRYMODE; // Don't update cull mode
if (gSP.changed || gDP.changed)
_updateStates();
updateArrays = OGL.renderState != RS_RECT;
if (updateArrays || scProgramChanged)
{
glDisableVertexAttribArray(SC_COLOR);
glDisableVertexAttribArray(SC_TEXCOORD0);
glDisableVertexAttribArray(SC_TEXCOORD1);
SC_ForceUniform1f(uRenderState, RS_RECT);
}
if (updateArrays)
{
glVertexAttrib4f(SC_POSITION, 0, 0, gSP.viewport.nearz, 1.0);
glVertexAttribPointer(SC_POSITION, 2, GL_FLOAT, GL_FALSE, sizeof(GLVertex), &OGL.rect[0].x);
OGL.renderState = RS_RECT;
}
pCurrentBuffer = FrameBuffer_GetCurrent();
if (pCurrentBuffer == NULL)
glViewport(0, OGL_GetHeightOffset(), OGL_GetScreenWidth(), OGL_GetScreenHeight());
else
glViewport(0, 0, pCurrentBuffer->m_width * pCurrentBuffer->m_scaleX, pCurrentBuffer->m_height * pCurrentBuffer->m_scaleY);
glDisable(GL_CULL_FACE);
scaleX = pCurrentBuffer != NULL ? 1.0f / pCurrentBuffer->m_width : gln64VI.rwidth;
scaleY = pCurrentBuffer != NULL ? 1.0f / pCurrentBuffer->m_height : gln64VI.rheight;
Z = (gDP.otherMode.depthSource == G_ZS_PRIM) ? gDP.primDepth.z : gSP.viewport.nearz;
W = 1.0f;
OGL.rect[0].x = (float)ulx * (2.0f * scaleX) - 1.0f;
OGL.rect[0].y = (float)uly * (-2.0f * scaleY) + 1.0f;
OGL.rect[0].z = Z;
OGL.rect[0].w = W;
OGL.rect[1].x = (float)lrx * (2.0f * scaleX) - 1.0f;
OGL.rect[1].y = OGL.rect[0].y;
OGL.rect[1].z = Z;
OGL.rect[1].w = W;
OGL.rect[2].x = OGL.rect[0].x;
OGL.rect[2].y = (float)lry * (-2.0f * scaleY) + 1.0f;
OGL.rect[2].z = Z;
OGL.rect[2].w = W;
OGL.rect[3].x = OGL.rect[1].x;
OGL.rect[3].y = OGL.rect[2].y;
OGL.rect[3].z = Z;
OGL.rect[3].w = W;
#if 0
if (ogl.isAdjustScreen() && (gDP.colorImage.width > gln64VI.width * 98 / 100) && (_lrx - _ulx < gln64VI.width * 9 / 10)) {
const float scale = ogl.getAdjustScale();
for (uint32_t i = 0; i < 4; ++i)
m_rect[i].x *= scale;
}
#endif
if (gDP.otherMode.cycleType == G_CYC_FILL)
glVertexAttrib4fv(SC_COLOR, color);
else
glVertexAttrib4f(SC_COLOR, 0.0f, 0.0f, 0.0f, 0.0f);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
gSP.changed |= CHANGED_GEOMETRYMODE | CHANGED_VIEWPORT;
}
static
bool texturedRectShadowMap(const struct TexturedRectParams *a)
{
struct FrameBuffer *pCurrentBuffer = FrameBuffer_GetCurrent();
if (pCurrentBuffer != NULL)
{
if (gDP.textureImage.size == 2 && gDP.textureImage.address >= gDP.depthImageAddress && gDP.textureImage.address < (gDP.depthImageAddress + gDP.colorImage.width*gDP.colorImage.width * 6 / 4))
{
#ifdef NEW
pCurrentBuffer->m_pDepthBuffer->activateDepthBufferTexture(pCurrentBuffer);
SetDepthFogCombiner();
#endif
}
}
return false;
}
static
bool texturedRectDepthBufferCopy(const struct TexturedRectParams *_params)
{
// Copy one line from depth buffer into auxiliary color buffer with height = 1.
// Data from depth buffer loaded into TMEM and then rendered to RDRAM by texrect.
// Works only with depth buffer emulation enabled.
// Load of arbitrary data to that area causes weird camera rotation in CBFD.
const struct gDPTile *pTile = (const struct gDPTile*)gSP.textureTile[0];
if (pTile->loadType == LOADTYPE_BLOCK && gDP.textureImage.size == 2
&& gDP.textureImage.address >= gDP.depthImageAddress
&& gDP.textureImage.address < (gDP.depthImageAddress + gDP.colorImage.width*gDP.colorImage.width * 6 / 4))
{
uint32_t x;
uint32_t width, ulx;
uint16_t *pSrc, *pDst;
struct FrameBuffer *pBuffer = FrameBuffer_GetCurrent();
if (config.frameBufferEmulation.enable == 0 || !pBuffer)
return true;
/* TODO/FIXMES */
#ifdef NEW
pBuffer->m_cleared = true;
#endif
if (config.frameBufferEmulation.copyDepthToRDRAM == 0)
return true;
#ifdef NEW
if (FrameBuffer_CopyDepthBuffer(gDP.colorImage.address))
RDP_RepeatLastLoadBlock();
#endif
width = (uint32_t)(_params->lrx - _params->ulx);
ulx = (uint32_t)_params->ulx;
pSrc = ((uint16_t*)TMEM) + (uint32_t)floorf(_params->uls + 0.5f);
pDst = (uint16_t*)(gfx_info.RDRAM + gDP.colorImage.address);
for (x = 0; x < width; ++x)
pDst[(ulx + x) ^ 1] = swapword(pSrc[x]);
return true;
}
return false;
}
static
bool texturedRectCopyToItself(const struct TexturedRectParams * _params)
{
struct FrameBuffer *pCurrent = FrameBuffer_GetCurrent();
if (gSP.textureTile[0]->frameBuffer == pCurrent)
return true;
return texturedRectDepthBufferCopy(_params);
}
static bool texturedRectBGCopy(const struct TexturedRectParams *_params)
{
uint8_t *texaddr, *fbaddr;
uint32_t y, width, tex_width, uly, lry;
float flry;
if (GBI_GetCurrentMicrocodeType() != S2DEX)
return false;
flry = _params->lry;
if (flry > gDP.scissor.lry)
flry = gDP.scissor.lry;
width = (uint32_t)(_params->lrx - _params->ulx);
tex_width = gSP.textureTile[0]->line << 3;
uly = (uint32_t)_params->uly;
lry = flry;
texaddr = gfx_info.RDRAM + gDP.loadInfo[gSP.textureTile[0]->tmem].texAddress + tex_width*(uint32_t)_params->ult + (uint32_t)_params->uls;
fbaddr = gfx_info.RDRAM + gDP.colorImage.address + (uint32_t)_params->ulx;
for (y = uly; y < lry; ++y)
{
uint8_t *src = texaddr + (y - uly) * tex_width;
uint8_t *dst = fbaddr + y * gDP.colorImage.width;
memcpy(dst, src, width);
}
FrameBuffer_RemoveBuffer(gDP.colorImage.address);
return true;
}
static bool texturedRectPaletteMod(const struct TexturedRectParams *_params)
{
uint32_t i;
uint16_t env16, prim16, *src, *dst;
uint8_t envr, envg, envb, prmr, prmg, prmb;
if (gDP.scissor.lrx != 16 || gDP.scissor.lry != 1 || _params->lrx != 16 || _params->lry != 1)
return false;
envr = (uint8_t)(gDP.envColor.r * 31.0f);
envg = (uint8_t)(gDP.envColor.g * 31.0f);
envb = (uint8_t)(gDP.envColor.b * 31.0f);
env16 = (uint16_t)((envr << 11) | (envg << 6) | (envb << 1) | 1);
prmr = (uint8_t)(gDP.primColor.r * 31.0f);
prmg = (uint8_t)(gDP.primColor.g * 31.0f);
prmb = (uint8_t)(gDP.primColor.b * 31.0f);
prim16 = (uint16_t)((prmr << 11) | (prmg << 6) | (prmb << 1) | 1);
src = (uint16_t*)&TMEM[256];
dst = (uint16_t*)(gfx_info.RDRAM + gDP.colorImage.address);
for (i = 0; i < 16; ++i)
dst[i ^ 1] = (src[i<<2] & 0x100) ? prim16 : env16;
return true;
}
static
bool texturedRectMonochromeBackground(const struct TexturedRectParams * _params)
{
if (gDP.textureImage.address >= gDP.colorImage.address && gDP.textureImage.address <= (gDP.colorImage.address + gDP.colorImage.width*gDP.colorImage.height * 2)) {
#ifdef GL_IMAGE_TEXTURES_SUPPORT
FrameBuffer * pCurrentBuffer = frameBufferList().getCurrent();
if (pCurrentBuffer != NULL) {
FrameBuffer_ActivateBufferTexture(0, pCurrentBuffer);
SetMonochromeCombiner();
return false;
} else
#endif
return true;
}
return false;
}
// Special processing of textured rect.
// Return true if actuial rendering is not necessary
bool(*texturedRectSpecial)(const struct TexturedRectParams * _params) = NULL;
void OGL_DrawTexturedRect(const struct TexturedRectParams *_params)
{
struct FrameBuffer *pCurrentBuffer;
float scaleX, scaleY, Z, W;
bool updateArrays;
if (gSP.changed || gDP.changed)
_updateStates();
updateArrays = OGL.renderState != RS_TEXTUREDRECT;
if (updateArrays || scProgramChanged)
{
OGL.renderState = RS_TEXTUREDRECT;
glDisableVertexAttribArray(SC_COLOR);
OGL_SetTexCoordArrays();
SC_ForceUniform1f(uRenderState, RS_TEXTUREDRECT);
}
if (updateArrays)
{
glVertexAttrib4f(SC_COLOR, 0, 0, 0, 0);
glVertexAttribPointer(SC_POSITION, 2, GL_FLOAT, GL_FALSE, sizeof(GLVertex), &OGL.rect[0].x);
glVertexAttribPointer(SC_TEXCOORD0, 2, GL_FLOAT, GL_FALSE, sizeof(GLVertex), &OGL.rect[0].s0);
glVertexAttribPointer(SC_TEXCOORD1, 2, GL_FLOAT, GL_FALSE, sizeof(GLVertex), &OGL.rect[0].s1);
}
if (__RSP.cmd == 0xE4 && texturedRectSpecial != NULL && texturedRectSpecial(_params))
{
gSP.changed |= CHANGED_GEOMETRYMODE;
return;
}
pCurrentBuffer = FrameBuffer_GetCurrent();
if (pCurrentBuffer == NULL)
glViewport(0, OGL_GetHeightOffset(), OGL_GetScreenWidth(), OGL_GetScreenHeight());
else
glViewport(0, 0, pCurrentBuffer->m_width * pCurrentBuffer->m_scaleX, pCurrentBuffer->m_height * pCurrentBuffer->m_scaleY);
glDisable(GL_CULL_FACE);
scaleX = pCurrentBuffer != NULL ? 1.0f / pCurrentBuffer->m_width : gln64VI.rwidth;
scaleY = pCurrentBuffer != NULL ? 1.0f / pCurrentBuffer->m_height : gln64VI.rheight;
Z = (gDP.otherMode.depthSource == G_ZS_PRIM) ? gDP.primDepth.z : gSP.viewport.nearz;
W = 1.0f;
OGL.rect[0].x = (float) _params->ulx * (2.0f * scaleX) - 1.0f;
OGL.rect[0].y = (float) _params->uly * (-2.0f * scaleY) + 1.0f;
OGL.rect[0].z = Z;
OGL.rect[0].w = W;
OGL.rect[1].x = (float) (_params->lrx) * (2.0f * scaleX) - 1.0f;
OGL.rect[1].y = OGL.rect[0].y;
OGL.rect[1].z = Z;
OGL.rect[1].w = W;
OGL.rect[2].x = OGL.rect[0].x;
OGL.rect[2].y = (float) (_params->lry) * (-2.0f * scaleY) + 1.0f;
OGL.rect[2].z = Z;
OGL.rect[2].w = W;
OGL.rect[3].x = OGL.rect[1].x;
OGL.rect[3].y = OGL.rect[2].y;
OGL.rect[3].z = Z;
OGL.rect[3].w = W;
if (scProgramCurrent->usesT0 && cache.current[0] && gSP.textureTile[0])
{
OGL.rect[0].s0 = _params->uls * cache.current[0]->shiftScaleS - gSP.textureTile[0]->fuls;
OGL.rect[0].t0 = _params->ult * cache.current[0]->shiftScaleT - gSP.textureTile[0]->fult;
OGL.rect[3].s0 = (_params->lrs + 1.0f) * cache.current[0]->shiftScaleS - gSP.textureTile[0]->fuls;
OGL.rect[3].t0 = (_params->lrt + 1.0f) * cache.current[0]->shiftScaleT - gSP.textureTile[0]->fult;
if ((cache.current[0]->maskS) && !(cache.current[0]->mirrorS) && (fmod( OGL.rect[0].s0, cache.current[0]->width ) == 0.0f))
{
OGL.rect[3].s0 -= OGL.rect[0].s0;
OGL.rect[0].s0 = 0.0f;
}
if ((cache.current[0]->maskT) && !(cache.current[0]->mirrorT) && (fmod( OGL.rect[0].t0, cache.current[0]->height ) == 0.0f))
{
OGL.rect[3].t0 -= OGL.rect[0].t0;
OGL.rect[0].t0 = 0.0f;
}
glActiveTexture( GL_TEXTURE0);
if ((OGL.rect[0].s0 >= 0.0f) && (OGL.rect[3].s0 <= cache.current[0]->width))
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
if ((OGL.rect[0].t0 >= 0.0f) && (OGL.rect[3].t0 <= cache.current[0]->height))
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
OGL.rect[0].s0 *= cache.current[0]->scaleS;
OGL.rect[0].t0 *= cache.current[0]->scaleT;
OGL.rect[3].s0 *= cache.current[0]->scaleS;
OGL.rect[3].t0 *= cache.current[0]->scaleT;
}
if (scProgramCurrent->usesT1 && cache.current[1] && gSP.textureTile[1])
{
OGL.rect[0].s1 = _params->uls * cache.current[1]->shiftScaleS - gSP.textureTile[1]->fuls;
OGL.rect[0].t1 = _params->ult * cache.current[1]->shiftScaleT - gSP.textureTile[1]->fult;
OGL.rect[3].s1 = (_params->lrs + 1.0f) * cache.current[1]->shiftScaleS - gSP.textureTile[1]->fuls;
OGL.rect[3].t1 = (_params->lrt + 1.0f) * cache.current[1]->shiftScaleT - gSP.textureTile[1]->fult;
if ((cache.current[1]->maskS) && (fmod( OGL.rect[0].s1, cache.current[1]->width ) == 0.0f) && !(cache.current[1]->mirrorS))
{
OGL.rect[3].s1 -= OGL.rect[0].s1;
OGL.rect[0].s1 = 0.0f;
}
if ((cache.current[1]->maskT) && (fmod( OGL.rect[0].t1, cache.current[1]->height ) == 0.0f) && !(cache.current[1]->mirrorT))
{
OGL.rect[3].t1 -= OGL.rect[0].t1;
OGL.rect[0].t1 = 0.0f;
}
glActiveTexture( GL_TEXTURE1);
if ((OGL.rect[0].s1 == 0.0f) && (OGL.rect[3].s1 <= cache.current[1]->width))
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
if ((OGL.rect[0].t1 == 0.0f) && (OGL.rect[3].t1 <= cache.current[1]->height))
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
OGL.rect[0].s1 *= cache.current[1]->scaleS;
OGL.rect[0].t1 *= cache.current[1]->scaleT;
OGL.rect[3].s1 *= cache.current[1]->scaleS;
OGL.rect[3].t1 *= cache.current[1]->scaleT;
}
if (gDP.otherMode.cycleType == G_CYC_COPY)
{
glActiveTexture(GL_TEXTURE0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
}
if (_params->flip)
{
OGL.rect[1].s0 = OGL.rect[0].s0;
OGL.rect[1].t0 = OGL.rect[3].t0;
OGL.rect[1].s1 = OGL.rect[0].s1;
OGL.rect[1].t1 = OGL.rect[3].t1;
OGL.rect[2].s0 = OGL.rect[3].s0;
OGL.rect[2].t0 = OGL.rect[0].t0;
OGL.rect[2].s1 = OGL.rect[3].s1;
OGL.rect[2].t1 = OGL.rect[0].t1;
}
else
{
OGL.rect[1].s0 = OGL.rect[3].s0;
OGL.rect[1].t0 = OGL.rect[0].t0;
OGL.rect[1].s1 = OGL.rect[3].s1;
OGL.rect[1].t1 = OGL.rect[0].t1;
OGL.rect[2].s0 = OGL.rect[0].s0;
OGL.rect[2].t0 = OGL.rect[3].t0;
OGL.rect[2].s1 = OGL.rect[0].s1;
OGL.rect[2].t1 = OGL.rect[3].t1;
}
#ifdef NEW
if (ogl.isAdjustScreen() && (gDP.colorImage.width > gln64VI.width * 98 / 100) && (_params.lrx - _params.ulx < gln64VI.width * 9 / 10))
{
const float scale = ogl.getAdjustScale();
for (uint32_t i = 0; i < 4; ++i)
m_rect[i].x *= scale;
}
#endif
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
gSP.changed |= CHANGED_GEOMETRYMODE | CHANGED_VIEWPORT;
}
void OGL_ClearDepthBuffer(bool _fullsize)
{
if (config.frameBufferEmulation.enable && FrameBuffer_GetCurrent() == NULL)
return;
#ifdef NEW
depthBufferList().clearBuffer(_uly, _lry);
#endif
glDisable( GL_SCISSOR_TEST );
glDepthMask( GL_TRUE );
glClear( GL_DEPTH_BUFFER_BIT );
_updateDepthUpdate();
glEnable( GL_SCISSOR_TEST );
}
void OGL_ClearColorBuffer(float *color)
{
glDisable( GL_SCISSOR_TEST );
glClearColor( color[0], color[1], color[2], color[3] );
glClear( GL_COLOR_BUFFER_BIT );
glEnable( GL_SCISSOR_TEST );
}
int OGL_CheckError(void)
{
GLenum e = glGetError();
if (e != GL_NO_ERROR)
{
printf("GL Error: ");
switch(e)
{
case GL_INVALID_ENUM: printf("INVALID ENUM"); break;
case GL_INVALID_VALUE: printf("INVALID VALUE"); break;
case GL_INVALID_OPERATION: printf("INVALID OPERATION"); break;
case GL_OUT_OF_MEMORY: printf("OUT OF MEMORY"); break;
}
printf("\n");
return 1;
}
return 0;
}
void OGL_SwapBuffers(void)
{
int retro_return(bool a);
// if emulator defined a render callback function, call it before
// buffer swap
if (renderCallback)
(*renderCallback)();
retro_return(true);
scProgramChanged = 0;
gDP.otherMode.l = 0;
gln64gDPSetTextureLUT(G_TT_NONE);
++__RSP.DList;
}
void OGL_ReadScreen( void *dest, int *width, int *height )
{
*width = OGL_GetScreenWidth();
*height = OGL_GetScreenHeight();
/* ReadScreen2 contract: a NULL dest is a dimension query; otherwise
* dest is a caller-owned width*height*3 buffer expecting tightly
* packed RGB rows. The old code instead leaked a malloc into the
* by-value parameter - the caller's buffer was never written - and
* read GL_RGBA, overflowing its own *3-sized allocation. */
if (!dest)
return;
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, OGL_GetHeightOffset(),
OGL_GetScreenWidth(), OGL_GetScreenHeight(),
GL_RGB, GL_UNSIGNED_BYTE, dest );
glPixelStorei(GL_PACK_ALIGNMENT, 4);
}
void _setSpecialTexrect(void)
{
const char * name = __RSP.romname;
if (strstr(name, (const char *)"Beetle") || strstr(name, (const char *)"BEETLE") || strstr(name, (const char *)"HSV")
|| strstr(name, (const char *)"DUCK DODGERS") || strstr(name, (const char *)"DAFFY DUCK"))
texturedRectSpecial = texturedRectShadowMap;
else if (strstr(name, (const char *)"Perfect Dark") || strstr(name, (const char *)"PERFECT DARK"))
texturedRectSpecial = texturedRectDepthBufferCopy; // See comments to that function!
else if (strstr(name, (const char *)"CONKER BFD"))
texturedRectSpecial = texturedRectCopyToItself;
else if (strstr(name, (const char *)"YOSHI STORY"))
texturedRectSpecial = texturedRectBGCopy;
else if (strstr(name, (const char *)"PAPER MARIO") || strstr(name, (const char *)"MARIO STORY"))
texturedRectSpecial = texturedRectPaletteMod;
else if (strstr(name, (const char *)"ZELDA"))
texturedRectSpecial = texturedRectMonochromeBackground;
else
texturedRectSpecial = NULL;
}
bool OGL_Start(void)
{
float f;
_initStates();
_setSpecialTexrect();
//check extensions
if ((config.texture.maxAnisotropy>0) && !OGL_IsExtSupported("GL_EXT_texture_filter_anistropic"))
{
LOG(LOG_WARNING, "Anistropic Filtering is not supported.\n");
config.texture.maxAnisotropy = 0;
}
f = 0;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &f);
if (config.texture.maxAnisotropy > ((int)f))
{
LOG(LOG_WARNING, "Clamping max anistropy to %ix.\n", (int)f);
config.texture.maxAnisotropy = (int)f;
}
//Print some info
LOG(LOG_VERBOSE, "[gles2n64]: Enable Runfast... \n");
//We must have a shader bound before binding any textures:
Combiner_Init();
Combiner_Set(EncodeCombineMode(0, 0, 0, TEXEL0, 0, 0, 0, TEXEL0, 0, 0, 0, TEXEL0, 0, 0, 0, TEXEL0), -1);
Combiner_Set(EncodeCombineMode(0, 0, 0, SHADE, 0, 0, 0, 1, 0, 0, 0, SHADE, 0, 0, 0, 1), -1);
TextureCache_Init();
OGL.renderState = RS_NONE;
gSP.changed = gDP.changed = 0xFFFFFFFF;
memset(OGL.triangles.vertices, 0, VERTBUFF_SIZE * sizeof(struct SPVertex));
memset(OGL.triangles.elements, 0, ELEMBUFF_SIZE * sizeof(GLubyte));
OGL.triangles.num = 0;
return true;
}