mirror of
https://github.com/blawar/GLideN64.git
synced 2024-07-04 10:03:36 +00:00
![Sergey Lipskiy](/assets/img/avatar_default.png)
How it work: - content of previous main frame buffer is copied into depth buffer area. - CPU applies blur to th eimage in depth buffer area. - Depth buffer is used as background texture for pause screen. Solution: 1. Do not use depth buffer FBO as texture. Image must be load from RDRAM. 2. If current color image is depth image and previous color buffer is used as texture for it, copy content of color image in RDRAM to depth image area.
1097 lines
34 KiB
C++
1097 lines
34 KiB
C++
#include <assert.h>
|
|
#include <math.h>
|
|
#include "OpenGL.h"
|
|
#include "FrameBuffer.h"
|
|
#include "DepthBuffer.h"
|
|
#include "N64.h"
|
|
#include "RSP.h"
|
|
#include "RDP.h"
|
|
#include "gDP.h"
|
|
#include "VI.h"
|
|
#include "Textures.h"
|
|
#include "Combiner.h"
|
|
#include "GLSLCombiner.h"
|
|
#include "Types.h"
|
|
#include "Config.h"
|
|
#include "Debug.h"
|
|
#include "PostProcessor.h"
|
|
|
|
using namespace std;
|
|
|
|
#ifndef GLES2
|
|
const GLint monohromeInternalformat = GL_R8;
|
|
const GLenum monohromeformat = GL_RED;
|
|
#else
|
|
const GLint monohromeInternalformat = GL_LUMINANCE;
|
|
const GLenum monohromeformat = GL_LUMINANCE;
|
|
#endif // GLES2
|
|
|
|
#ifndef GLES2
|
|
class FrameBufferToRDRAM
|
|
{
|
|
public:
|
|
FrameBufferToRDRAM() :
|
|
m_FBO(0), m_PBO(0), m_pTexture(NULL)
|
|
{}
|
|
|
|
void Init();
|
|
void Destroy();
|
|
|
|
void CopyToRDRAM(u32 _address);
|
|
|
|
private:
|
|
struct RGBA {
|
|
u8 r, g, b, a;
|
|
};
|
|
|
|
GLuint m_FBO;
|
|
GLuint m_PBO;
|
|
CachedTexture * m_pTexture;
|
|
};
|
|
|
|
class DepthBufferToRDRAM
|
|
{
|
|
public:
|
|
DepthBufferToRDRAM() :
|
|
m_FBO(0), m_PBO(0), m_pColorTexture(NULL), m_pDepthTexture(NULL), m_lastDList(0)
|
|
{}
|
|
|
|
void Init();
|
|
void Destroy();
|
|
|
|
bool CopyToRDRAM( u32 address );
|
|
|
|
private:
|
|
GLuint m_FBO;
|
|
GLuint m_PBO;
|
|
CachedTexture * m_pColorTexture;
|
|
CachedTexture * m_pDepthTexture;
|
|
u32 m_lastDList;
|
|
};
|
|
#endif // GLES2
|
|
|
|
class RDRAMtoFrameBuffer
|
|
{
|
|
public:
|
|
RDRAMtoFrameBuffer() : m_pTexture(NULL), m_PBO(0) {}
|
|
|
|
void Init();
|
|
void Destroy();
|
|
|
|
void CopyFromRDRAM( u32 _address, bool _bUseAlpha);
|
|
|
|
private:
|
|
CachedTexture * m_pTexture;
|
|
#ifndef GLES2
|
|
GLuint m_PBO;
|
|
#else
|
|
GLubyte* m_PBO;
|
|
#endif
|
|
};
|
|
|
|
#ifndef GLES2
|
|
FrameBufferToRDRAM g_fbToRDRAM;
|
|
DepthBufferToRDRAM g_dbToRDRAM;
|
|
#endif
|
|
RDRAMtoFrameBuffer g_RDRAMtoFB;
|
|
|
|
FrameBuffer::FrameBuffer() : m_cleared(false), m_isDepthBuffer(false), m_copiedToRDRAM(false), m_pLoadTile(NULL), m_pDepthBuffer(NULL), m_pResolveTexture(NULL), m_resolveFBO(0), m_resolved(false)
|
|
{
|
|
m_pTexture = textureCache().addFrameBufferTexture();
|
|
glGenFramebuffers(1, &m_FBO);
|
|
}
|
|
|
|
FrameBuffer::FrameBuffer(FrameBuffer && _other) :
|
|
m_startAddress(_other.m_startAddress), m_endAddress(_other.m_endAddress),
|
|
m_size(_other.m_size), m_width(_other.m_width), m_height(_other.m_height), m_fillcolor(_other.m_fillcolor),
|
|
m_scaleX(_other.m_scaleX), m_scaleY(_other.m_scaleY), m_cleared(_other.m_cleared), m_cfb(_other.m_cfb), m_isDepthBuffer(_other.m_isDepthBuffer), m_copiedToRDRAM(_other.m_copiedToRDRAM),
|
|
m_FBO(_other.m_FBO), m_pLoadTile(_other.m_pLoadTile), m_pTexture(_other.m_pTexture), m_pDepthBuffer(_other.m_pDepthBuffer),
|
|
m_pResolveTexture(_other.m_pResolveTexture), m_resolveFBO(_other.m_resolveFBO), m_resolved(_other.m_resolved)
|
|
{
|
|
_other.m_FBO = 0;
|
|
_other.m_pTexture = NULL;
|
|
_other.m_pLoadTile = NULL;
|
|
_other.m_pDepthBuffer = NULL;
|
|
_other.m_pResolveTexture = NULL;
|
|
_other.m_resolveFBO = 0;
|
|
}
|
|
|
|
|
|
FrameBuffer::~FrameBuffer()
|
|
{
|
|
if (m_FBO != 0)
|
|
glDeleteFramebuffers(1, &m_FBO);
|
|
if (m_pTexture != NULL)
|
|
textureCache().removeFrameBufferTexture(m_pTexture);
|
|
if (m_resolveFBO != 0)
|
|
glDeleteFramebuffers(1, &m_resolveFBO);
|
|
if (m_pResolveTexture != NULL)
|
|
textureCache().removeFrameBufferTexture(m_pResolveTexture);
|
|
}
|
|
|
|
void FrameBuffer::_initTexture(u16 _format, u16 _size, CachedTexture *_pTexture)
|
|
{
|
|
_pTexture->width = (u32)(m_width * video().getScaleX());
|
|
_pTexture->height = (u32)(m_height * video().getScaleY());
|
|
_pTexture->format = _format;
|
|
_pTexture->size = _size;
|
|
_pTexture->clampS = 1;
|
|
_pTexture->clampT = 1;
|
|
_pTexture->address = m_startAddress;
|
|
_pTexture->clampWidth = m_width;
|
|
_pTexture->clampHeight = m_height;
|
|
_pTexture->frameBufferTexture = TRUE;
|
|
_pTexture->maskS = 0;
|
|
_pTexture->maskT = 0;
|
|
_pTexture->mirrorS = 0;
|
|
_pTexture->mirrorT = 0;
|
|
_pTexture->realWidth = _pTexture->width;
|
|
_pTexture->realHeight = _pTexture->height;
|
|
_pTexture->textureBytes = _pTexture->realWidth * _pTexture->realHeight;
|
|
if (_size > G_IM_SIZ_8b)
|
|
_pTexture->textureBytes *= 4;
|
|
textureCache().addFrameBufferTextureSize(_pTexture->textureBytes);
|
|
}
|
|
|
|
void FrameBuffer::_setAndAttachTexture(u16 _size, CachedTexture *_pTexture)
|
|
{
|
|
glBindTexture(GL_TEXTURE_2D, _pTexture->glName);
|
|
if (_size > G_IM_SIZ_8b)
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _pTexture->realWidth, _pTexture->realHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
|
else
|
|
glTexImage2D(GL_TEXTURE_2D, 0, monohromeInternalformat, _pTexture->realWidth, _pTexture->realHeight, 0, monohromeformat, GL_UNSIGNED_BYTE, NULL);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _pTexture->glName, 0);
|
|
}
|
|
|
|
void FrameBuffer::init(u32 _address, u32 _endAddress, u16 _format, u16 _size, u16 _width, u16 _height, bool _cfb)
|
|
{
|
|
OGLVideo & ogl = video();
|
|
m_startAddress = _address;
|
|
m_endAddress = _endAddress;
|
|
m_width = _width;
|
|
m_height = _height;
|
|
m_size = _size;
|
|
m_scaleX = ogl.getScaleX();
|
|
m_scaleY = ogl.getScaleY();
|
|
m_fillcolor = 0;
|
|
m_cfb = _cfb;
|
|
|
|
_initTexture(_format, _size, m_pTexture);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_FBO);
|
|
|
|
if (config.video.multisampling != 0) {
|
|
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, m_pTexture->glName);
|
|
if (_size > G_IM_SIZ_8b)
|
|
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, config.video.multisampling, GL_RGBA8, m_pTexture->realWidth, m_pTexture->realHeight, false);
|
|
else
|
|
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, config.video.multisampling, monohromeInternalformat, m_pTexture->realWidth, m_pTexture->realHeight, false);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, m_pTexture->glName, 0);
|
|
|
|
m_pResolveTexture = textureCache().addFrameBufferTexture();
|
|
_initTexture(_format, _size, m_pResolveTexture);
|
|
glGenFramebuffers(1, &m_resolveFBO);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_resolveFBO);
|
|
_setAndAttachTexture(_size, m_pResolveTexture);
|
|
assert(checkFBO());
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_FBO);
|
|
} else
|
|
_setAndAttachTexture(_size, m_pTexture);
|
|
}
|
|
|
|
void FrameBuffer::resolveMultisampledTexture()
|
|
{
|
|
if (m_resolved)
|
|
return;
|
|
glDisable(GL_SCISSOR_TEST);
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_FBO);
|
|
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_resolveFBO);
|
|
glBlitFramebuffer(
|
|
0, 0, m_pTexture->realWidth, m_pTexture->realHeight,
|
|
0, 0, m_pResolveTexture->realWidth, m_pResolveTexture->realHeight,
|
|
GL_COLOR_BUFFER_BIT, GL_NEAREST
|
|
);
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBufferList().getCurrent()->m_FBO);
|
|
glEnable(GL_SCISSOR_TEST);
|
|
m_resolved = true;
|
|
}
|
|
|
|
CachedTexture * FrameBuffer::getTexture()
|
|
{
|
|
if (config.video.multisampling == 0)
|
|
return m_pTexture;
|
|
if (m_resolved)
|
|
return m_pResolveTexture;
|
|
|
|
resolveMultisampledTexture();
|
|
return m_pResolveTexture;
|
|
}
|
|
|
|
void FrameBufferList::init()
|
|
{
|
|
m_pCurrent = NULL;
|
|
}
|
|
|
|
void FrameBufferList::destroy() {
|
|
m_list.clear();
|
|
m_pCurrent = NULL;
|
|
m_drawBuffer = GL_BACK;
|
|
}
|
|
|
|
FrameBuffer * FrameBufferList::findBuffer(u32 _startAddress)
|
|
{
|
|
for (FrameBuffers::iterator iter = m_list.begin(); iter != m_list.end(); ++iter)
|
|
if (iter->m_startAddress <= _startAddress && iter->m_endAddress >= _startAddress) // [ { ]
|
|
return &(*iter);
|
|
return NULL;
|
|
}
|
|
|
|
FrameBuffer * FrameBufferList::_findBuffer(u32 _startAddress, u32 _endAddress, u32 _width)
|
|
{
|
|
if (m_list.empty())
|
|
return NULL;
|
|
|
|
FrameBuffers::iterator iter = m_list.end();
|
|
do {
|
|
--iter;
|
|
if ((iter->m_startAddress <= _startAddress && iter->m_endAddress >= _startAddress) || // [ { ]
|
|
(_startAddress <= iter->m_startAddress && _endAddress >= iter->m_startAddress)) { // { [ }
|
|
|
|
if (_startAddress != iter->m_startAddress || _width != iter->m_width) {
|
|
m_list.erase(iter);
|
|
return _findBuffer(_startAddress, _endAddress, _width);
|
|
}
|
|
|
|
return &(*iter);
|
|
}
|
|
} while (iter != m_list.begin());
|
|
return NULL;
|
|
}
|
|
|
|
FrameBuffer * FrameBufferList::findTmpBuffer(u32 _address)
|
|
{
|
|
for (FrameBuffers::iterator iter = m_list.begin(); iter != m_list.end(); ++iter)
|
|
if (iter->m_startAddress > _address || iter->m_endAddress < _address)
|
|
return &(*iter);
|
|
return NULL;
|
|
}
|
|
|
|
void FrameBufferList::saveBuffer(u32 _address, u16 _format, u16 _size, u16 _width, u16 _height, bool _cfb)
|
|
{
|
|
if (VI.width == 0 || VI.height == 0 || _height == 0) // H width is zero. Don't save
|
|
return;
|
|
OGLVideo & ogl = video();
|
|
m_drawBuffer = GL_FRAMEBUFFER;
|
|
if (m_pCurrent != NULL && gDP.colorImage.height > 0) {
|
|
m_pCurrent->m_endAddress = min(RDRAMSize, m_pCurrent->m_startAddress + (((m_pCurrent->m_width * gDP.colorImage.height) << m_pCurrent->m_size >> 1) - 1));
|
|
if (!config.frameBufferEmulation.copyToRDRAM && !config.frameBufferEmulation.copyFromRDRAM && !m_pCurrent->m_cfb && !m_pCurrent->m_cleared && gDP.colorImage.height > 1)
|
|
gDPFillRDRAM(m_pCurrent->m_startAddress, 0, 0, m_pCurrent->m_width, gDP.colorImage.height, m_pCurrent->m_width, m_pCurrent->m_size, m_pCurrent->m_fillcolor, false);
|
|
}
|
|
|
|
const u32 endAddress = _address + ((_width * (_height - 1)) << _size >> 1) - 1;
|
|
if (m_pCurrent == NULL || m_pCurrent->m_startAddress != _address || m_pCurrent->m_width != _width)
|
|
m_pCurrent = _findBuffer(_address, endAddress, _width);
|
|
if (m_pCurrent != NULL) {
|
|
if ((m_pCurrent->m_startAddress != _address) ||
|
|
(m_pCurrent->m_width != _width) ||
|
|
//(current->height != height) ||
|
|
//(current->size != size) || // TODO FIX ME
|
|
(m_pCurrent->m_scaleX != ogl.getScaleX()) ||
|
|
(m_pCurrent->m_scaleY != ogl.getScaleY()))
|
|
{
|
|
removeBuffer(m_pCurrent->m_startAddress);
|
|
m_pCurrent = NULL;
|
|
} else {
|
|
m_pCurrent->m_resolved = false;
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_pCurrent->m_FBO);
|
|
if (m_pCurrent->m_size != _size) {
|
|
f32 fillColor[4];
|
|
gDPGetFillColor(fillColor);
|
|
ogl.getRender().clearColorBuffer(fillColor);
|
|
m_pCurrent->m_size = _size;
|
|
m_pCurrent->m_pTexture->format = _format;
|
|
m_pCurrent->m_pTexture->size = _size;
|
|
if (m_pCurrent->m_pResolveTexture != NULL) {
|
|
m_pCurrent->m_pResolveTexture->format = _format;
|
|
m_pCurrent->m_pResolveTexture->size = _size;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
const bool bNew = m_pCurrent == NULL;
|
|
if (bNew) {
|
|
// Wasn't found or removed, create a new one
|
|
m_list.emplace_front();
|
|
FrameBuffer & buffer = m_list.front();
|
|
buffer.init(_address, endAddress, _format, _size, _width, _height, _cfb);
|
|
m_pCurrent = &buffer;
|
|
}
|
|
|
|
attachDepthBuffer();
|
|
|
|
#ifdef DEBUG
|
|
DebugMsg( DEBUG_HIGH | DEBUG_HANDLED, "FrameBuffer_SaveBuffer( 0x%08X ); depth buffer is 0x%08X\n",
|
|
address, (depthBuffer.top != NULL && depthBuffer.top->renderbuf > 0) ? depthBuffer.top->address : 0
|
|
);
|
|
#endif
|
|
// HACK ALERT: Dirty hack for Mario Tennis score board
|
|
if (bNew && (m_pCurrent->m_startAddress == 0x13ba50 || m_pCurrent->m_startAddress == 0x264430))
|
|
g_RDRAMtoFB.CopyFromRDRAM(m_pCurrent->m_startAddress, false);
|
|
if (!_cfb)
|
|
*(u32*)&RDRAM[m_pCurrent->m_startAddress] = m_pCurrent->m_startAddress;
|
|
|
|
m_pCurrent->m_cleared = false;
|
|
m_pCurrent->m_isDepthBuffer = false;
|
|
m_pCurrent->m_copiedToRDRAM = false;
|
|
|
|
gSP.changed |= CHANGED_TEXTURE;
|
|
}
|
|
|
|
void FrameBufferList::removeBuffer(u32 _address )
|
|
{
|
|
for (FrameBuffers::iterator iter = m_list.begin(); iter != m_list.end(); ++iter)
|
|
if (iter->m_startAddress == _address) {
|
|
if (m_pCurrent != NULL && m_pCurrent->m_startAddress == _address)
|
|
m_pCurrent = NULL;
|
|
m_list.erase(iter);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void FrameBufferList::attachDepthBuffer()
|
|
{
|
|
if (m_pCurrent == NULL)
|
|
return;
|
|
|
|
DepthBuffer * pDepthBuffer = depthBufferList().getCurrent();
|
|
if (m_pCurrent->m_FBO > 0 && pDepthBuffer != NULL) {
|
|
pDepthBuffer->initDepthImageTexture(m_pCurrent);
|
|
pDepthBuffer->initDepthBufferTexture(m_pCurrent);
|
|
m_pCurrent->m_pDepthBuffer = pDepthBuffer;
|
|
pDepthBuffer->setDepthAttachment();
|
|
if (video().getRender().isImageTexturesSupported() && config.frameBufferEmulation.N64DepthCompare != 0)
|
|
pDepthBuffer->bindDepthImageTexture();
|
|
} else
|
|
m_pCurrent->m_pDepthBuffer = NULL;
|
|
|
|
#ifndef GLES2
|
|
GLuint attachments[1] = { GL_COLOR_ATTACHMENT0 };
|
|
glDrawBuffers(1, attachments);
|
|
#endif
|
|
assert(checkFBO());
|
|
}
|
|
|
|
void FrameBuffer_Init()
|
|
{
|
|
frameBufferList().init();
|
|
#ifndef GLES2
|
|
g_fbToRDRAM.Init();
|
|
g_dbToRDRAM.Init();
|
|
#endif
|
|
g_RDRAMtoFB.Init();
|
|
}
|
|
|
|
void FrameBuffer_Destroy()
|
|
{
|
|
g_RDRAMtoFB.Destroy();
|
|
#ifndef GLES2
|
|
g_dbToRDRAM.Destroy();
|
|
g_fbToRDRAM.Destroy();
|
|
#endif
|
|
frameBufferList().destroy();
|
|
}
|
|
|
|
#ifndef GLES2
|
|
void FrameBufferList::renderBuffer(u32 _address)
|
|
{
|
|
static s32 vStartPrev = 0;
|
|
|
|
if (VI.width == 0 || *REG.VI_WIDTH == 0) // H width is zero. Don't draw
|
|
return;
|
|
|
|
FrameBuffer *pBuffer = findBuffer(_address);
|
|
if (pBuffer == NULL)
|
|
return;
|
|
|
|
OGLVideo & ogl = video();
|
|
GLint srcY0, srcY1, dstY0, dstY1;
|
|
GLint X0, X1, Xwidth;
|
|
GLint srcPartHeight = 0;
|
|
GLint dstPartHeight = 0;
|
|
|
|
const f32 yScale = _FIXED2FLOAT(_SHIFTR(*REG.VI_Y_SCALE, 0, 12), 10);
|
|
s32 vEnd = _SHIFTR(*REG.VI_V_START, 0, 10);
|
|
s32 vStart = _SHIFTR(*REG.VI_V_START, 16, 10);
|
|
const s32 hEnd = _SHIFTR(*REG.VI_H_START, 0, 10);
|
|
const s32 hStart = _SHIFTR(*REG.VI_H_START, 16, 10);
|
|
const s32 vSync = (*REG.VI_V_SYNC) & 0x03FF;
|
|
const bool interlaced = (*REG.VI_STATUS & 0x40) != 0;
|
|
const bool isPAL = vSync > 550;
|
|
const s32 vShift = (isPAL ? 47 : 37);
|
|
dstY0 = vStart - vShift;
|
|
dstY0 >>= 1;
|
|
dstY0 &= -(dstY0 >= 0);
|
|
vStart >>= 1;
|
|
vEnd >>= 1;
|
|
const u32 vFullHeight = isPAL ? 288 : 240;
|
|
const u32 vCurrentHeight = vEnd - vStart;
|
|
const float dstScaleY = (float)ogl.getHeight() / float(vFullHeight);
|
|
|
|
bool isLowerField = false;
|
|
if (interlaced)
|
|
isLowerField = vStart > vStartPrev;
|
|
vStartPrev = vStart;
|
|
|
|
srcY0 = ((_address - pBuffer->m_startAddress) << 1 >> pBuffer->m_size) / (*REG.VI_WIDTH);
|
|
if (isLowerField) {
|
|
if (srcY0 > 0)
|
|
--srcY0;
|
|
if (dstY0 > 0)
|
|
--dstY0;
|
|
}
|
|
|
|
if (srcY0 + vCurrentHeight > vFullHeight) {
|
|
dstPartHeight = srcY0;
|
|
srcY0 = (GLint)(srcY0*yScale);
|
|
srcPartHeight = srcY0;
|
|
srcY1 = VI.real_height;
|
|
dstY1 = dstY0 + vCurrentHeight - dstPartHeight;
|
|
} else {
|
|
dstY0 += srcY0;
|
|
dstY1 = dstY0 + vCurrentHeight;
|
|
srcY0 = (GLint)(srcY0*yScale);
|
|
srcY1 = srcY0 + VI.real_height;
|
|
}
|
|
|
|
const f32 scaleX = _FIXED2FLOAT(_SHIFTR(*REG.VI_X_SCALE, 0, 12), 10);
|
|
const s32 h0 = (isPAL ? 128 : 108);
|
|
const s32 hx0 = max(0, hStart - h0);
|
|
const s32 hx1 = max(0, h0 + 640 - hEnd);
|
|
X0 = (GLint)(hx0 * scaleX * ogl.getScaleX());
|
|
Xwidth = (GLint)((min((f32)VI.width, (hEnd - hStart)*scaleX)) * ogl.getScaleX());
|
|
X1 = ogl.getWidth() - (GLint)(hx1 *scaleX * ogl.getScaleX());
|
|
|
|
PostProcessor::get().process(pBuffer);
|
|
// glDisable(GL_SCISSOR_TEST) does not affect glBlitFramebuffer, at least on AMD
|
|
GLint vOffset = ogl.isFullscreen() ? 0 : ogl.getHeightOffset();
|
|
glScissor(0, 0, ogl.getScreenWidth(), ogl.getScreenHeight() + vOffset);
|
|
glDisable(GL_SCISSOR_TEST);
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, pBuffer->m_FBO);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
//glDrawBuffer( GL_BACK );
|
|
float clearColor[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
|
ogl.getRender().clearColorBuffer(clearColor);
|
|
const float srcScaleY = ogl.getScaleY();
|
|
const GLint hOffset = (ogl.getScreenWidth() - ogl.getWidth()) / 2;
|
|
vOffset += (ogl.getScreenHeight() - ogl.getHeight()) / 2;
|
|
|
|
GLint srcCoord[4] = { 0, (GLint)(srcY0*srcScaleY), Xwidth, (GLint)(srcY1*srcScaleY) };
|
|
GLint dstCoord[4] = { X0 + hOffset, vOffset + (GLint)(dstY0*dstScaleY), hOffset + X1, vOffset + (GLint)(dstY1*dstScaleY) };
|
|
|
|
GLenum filter = GL_LINEAR;
|
|
if (config.video.multisampling != 0) {
|
|
if (X0 > 0 || dstPartHeight > 0 ||
|
|
(srcCoord[2] - srcCoord[0]) != (dstCoord[2] - dstCoord[0]) ||
|
|
(srcCoord[3] - srcCoord[1]) != (dstCoord[3] - dstCoord[1])) {
|
|
pBuffer->resolveMultisampledTexture();
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, pBuffer->m_resolveFBO);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
} else
|
|
filter = GL_NEAREST;
|
|
}
|
|
|
|
glBlitFramebuffer(
|
|
srcCoord[0], srcCoord[1], srcCoord[2], srcCoord[3],
|
|
dstCoord[0], dstCoord[1], dstCoord[2], dstCoord[3],
|
|
GL_COLOR_BUFFER_BIT, filter
|
|
);
|
|
|
|
if (dstPartHeight > 0) {
|
|
const u32 size = *REG.VI_STATUS & 3;
|
|
pBuffer = findBuffer(_address + (((*REG.VI_WIDTH)*VI.height)<<size>>1));
|
|
if (pBuffer != NULL) {
|
|
srcY0 = 0;
|
|
srcY1 = srcPartHeight;
|
|
dstY0 = dstY1;
|
|
dstY1 = dstY0 + dstPartHeight;
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, pBuffer->m_FBO);
|
|
glBlitFramebuffer(
|
|
0, (GLint)(srcY0*srcScaleY), ogl.getWidth(), (GLint)(srcY1*srcScaleY),
|
|
hOffset, vOffset + (GLint)(dstY0*dstScaleY), hOffset + ogl.getWidth(), vOffset + (GLint)(dstY1*dstScaleY),
|
|
GL_COLOR_BUFFER_BIT, filter
|
|
);
|
|
}
|
|
}
|
|
|
|
glEnable(GL_SCISSOR_TEST);
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_pCurrent->m_FBO);
|
|
ogl.swapBuffers();
|
|
gDP.changed |= CHANGED_SCISSOR;
|
|
}
|
|
#else
|
|
|
|
void FrameBufferList::renderBuffer(u32 _address)
|
|
{
|
|
if (VI.width == 0) // H width is zero. Don't draw
|
|
return;
|
|
FrameBuffer *pBuffer = findBuffer(_address);
|
|
if (pBuffer == NULL)
|
|
return;
|
|
|
|
CombinerInfo::get().setCombine( EncodeCombineMode( 0, 0, 0, TEXEL0, 0, 0, 0, 1, 0, 0, 0, TEXEL0, 0, 0, 0, 1 ) );
|
|
glDisable( GL_BLEND );
|
|
glDisable(GL_SCISSOR_TEST);
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDisable( GL_CULL_FACE );
|
|
glDisable( GL_POLYGON_OFFSET_FILL );
|
|
gSP.changed = gDP.changed = 0;
|
|
|
|
const u32 width = pBuffer->m_width;
|
|
const u32 height = pBuffer->m_height;
|
|
|
|
OGLVideo & ogl = video();
|
|
pBuffer->m_pTexture->scaleS = ogl.getScaleX() / (float)pBuffer->m_pTexture->realWidth;
|
|
pBuffer->m_pTexture->scaleT = ogl.getScaleY() / (float)pBuffer->m_pTexture->realHeight;
|
|
pBuffer->m_pTexture->shiftScaleS = 1.0f;
|
|
pBuffer->m_pTexture->shiftScaleT = 1.0f;
|
|
pBuffer->m_pTexture->offsetS = 0;
|
|
pBuffer->m_pTexture->offsetT = (float)height;
|
|
textureCache().activateTexture(0, pBuffer->m_pTexture);
|
|
gSP.textureTile[0]->fuls = gSP.textureTile[0]->fult = 0.0f;
|
|
currentCombiner()->updateTextureInfo(true);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
m_drawBuffer = GL_BACK;
|
|
OGLRender::TexturedRectParams params(0.0f, 0.0f, width, height, 0.0f, 0.0f, width - 1.0f, height - 1.0f, false);
|
|
ogl.getRender().drawTexturedRect(params);
|
|
ogl.swapBuffers();
|
|
m_drawBuffer = GL_FRAMEBUFFER;
|
|
glEnable(GL_SCISSOR_TEST);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_pCurrent->m_FBO);
|
|
gSP.changed |= CHANGED_TEXTURE | CHANGED_VIEWPORT;
|
|
gDP.changed |= CHANGED_COMBINE;
|
|
}
|
|
#endif
|
|
|
|
void FrameBuffer_ActivateBufferTexture(s16 t, FrameBuffer *pBuffer)
|
|
{
|
|
if (pBuffer == NULL || pBuffer->m_pTexture == NULL)
|
|
return;
|
|
|
|
CachedTexture *pTexture = pBuffer->getTexture();
|
|
|
|
pTexture->scaleS = video().getScaleX() / (float)pTexture->realWidth;
|
|
pTexture->scaleT = video().getScaleY() / (float)pTexture->realHeight;
|
|
|
|
if (gSP.textureTile[t]->shifts > 10)
|
|
pTexture->shiftScaleS = (float)(1 << (16 - gSP.textureTile[t]->shifts));
|
|
else if (gSP.textureTile[t]->shifts > 0)
|
|
pTexture->shiftScaleS = 1.0f / (float)(1 << gSP.textureTile[t]->shifts);
|
|
else
|
|
pTexture->shiftScaleS = 1.0f;
|
|
|
|
if (gSP.textureTile[t]->shiftt > 10)
|
|
pTexture->shiftScaleT = (float)(1 << (16 - gSP.textureTile[t]->shiftt));
|
|
else if (gSP.textureTile[t]->shiftt > 0)
|
|
pTexture->shiftScaleT = 1.0f / (float)(1 << gSP.textureTile[t]->shiftt);
|
|
else
|
|
pTexture->shiftScaleT = 1.0f;
|
|
|
|
const u32 shift = gSP.textureTile[t]->imageAddress - pBuffer->m_startAddress;
|
|
const u32 factor = pBuffer->m_width << pBuffer->m_size >> 1;
|
|
if (gSP.textureTile[t]->loadType == LOADTYPE_TILE)
|
|
{
|
|
pTexture->offsetS = (float)pBuffer->m_pLoadTile->uls;
|
|
pTexture->offsetT = (float)(pBuffer->m_height - (pBuffer->m_pLoadTile->ult + shift/factor));
|
|
}
|
|
else
|
|
{
|
|
pTexture->offsetS = (float)((shift % factor) >> pBuffer->m_size << 1);
|
|
pTexture->offsetT = (float)(pBuffer->m_height - shift/factor);
|
|
}
|
|
|
|
// frameBufferList().renderBuffer(pBuffer->m_startAddress);
|
|
textureCache().activateTexture(t, pTexture);
|
|
gDP.changed |= CHANGED_FB_TEXTURE;
|
|
}
|
|
|
|
void FrameBuffer_ActivateBufferTextureBG(s16 t, FrameBuffer *pBuffer )
|
|
{
|
|
if (pBuffer == NULL || pBuffer->m_pTexture == NULL)
|
|
return;
|
|
|
|
CachedTexture *pTexture = pBuffer->getTexture();
|
|
pTexture->scaleS = video().getScaleX() / (float)pTexture->realWidth;
|
|
pTexture->scaleT = video().getScaleY() / (float)pTexture->realHeight;
|
|
|
|
pTexture->shiftScaleS = 1.0f;
|
|
pTexture->shiftScaleT = 1.0f;
|
|
|
|
pTexture->offsetS = gSP.bgImage.imageX;
|
|
pTexture->offsetT = (float)pBuffer->m_height - gSP.bgImage.imageY;
|
|
|
|
// FrameBuffer_RenderBuffer(buffer->startAddress);
|
|
textureCache().activateTexture(t, pTexture);
|
|
gDP.changed |= CHANGED_FB_TEXTURE;
|
|
}
|
|
|
|
#ifndef GLES2
|
|
void FrameBufferToRDRAM::Init()
|
|
{
|
|
// generate a framebuffer
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
glGenFramebuffers(1, &m_FBO);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_FBO);
|
|
|
|
m_pTexture = textureCache().addFrameBufferTexture();
|
|
m_pTexture->format = G_IM_FMT_RGBA;
|
|
m_pTexture->clampS = 1;
|
|
m_pTexture->clampT = 1;
|
|
m_pTexture->frameBufferTexture = TRUE;
|
|
m_pTexture->maskS = 0;
|
|
m_pTexture->maskT = 0;
|
|
m_pTexture->mirrorS = 0;
|
|
m_pTexture->mirrorT = 0;
|
|
m_pTexture->realWidth = 640;
|
|
m_pTexture->realHeight = 480;
|
|
m_pTexture->textureBytes = 640 * 480 * 4;
|
|
textureCache().addFrameBufferTextureSize(m_pTexture->textureBytes);
|
|
glBindTexture( GL_TEXTURE_2D, m_pTexture->glName );
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_pTexture->realWidth, m_pTexture->realHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_pTexture->glName, 0);
|
|
// check if everything is OK
|
|
assert(checkFBO());
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
|
|
// Generate and initialize Pixel Buffer Objects
|
|
glGenBuffers(1, &m_PBO);
|
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, m_PBO);
|
|
glBufferData(GL_PIXEL_PACK_BUFFER, m_pTexture->textureBytes, NULL, GL_DYNAMIC_DRAW);
|
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
|
}
|
|
|
|
void FrameBufferToRDRAM::Destroy() {
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
if (m_FBO != 0) {
|
|
glDeleteFramebuffers(1, &m_FBO);
|
|
m_FBO = 0;
|
|
}
|
|
if (m_pTexture != NULL) {
|
|
textureCache().removeFrameBufferTexture(m_pTexture);
|
|
m_pTexture = NULL;
|
|
}
|
|
if (m_PBO != 0) {
|
|
glDeleteBuffers(1, &m_PBO);
|
|
m_PBO = 0;
|
|
}
|
|
}
|
|
|
|
void FrameBufferToRDRAM::CopyToRDRAM(u32 _address) {
|
|
if (VI.width == 0) // H width is zero. Don't copy
|
|
return;
|
|
FrameBuffer *pBuffer = frameBufferList().findBuffer(_address);
|
|
if (pBuffer == NULL || pBuffer->m_width < VI.width)
|
|
return;
|
|
|
|
_address = pBuffer->m_startAddress;
|
|
if (config.video.multisampling != 0) {
|
|
pBuffer->resolveMultisampledTexture();
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, pBuffer->m_resolveFBO);
|
|
} else
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, pBuffer->m_FBO);
|
|
glDisable(GL_SCISSOR_TEST);
|
|
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_FBO);
|
|
glBlitFramebuffer(
|
|
0, 0, video().getWidth(), video().getHeight(),
|
|
0, 0, VI.width, VI.height,
|
|
GL_COLOR_BUFFER_BIT, GL_LINEAR
|
|
);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBufferList().getCurrent()->m_FBO);
|
|
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_FBO);
|
|
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
|
#ifndef GLES2
|
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, m_PBO);
|
|
glReadPixels( 0, 0, VI.width, VI.height, GL_RGBA, GL_UNSIGNED_BYTE, 0 );
|
|
GLubyte* pixelData = (GLubyte*)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
|
|
if(pixelData == NULL) {
|
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
|
return;
|
|
}
|
|
#else
|
|
m_curIndex = 0;
|
|
const u32 nextIndex = 0;
|
|
GLubyte* pixelData = (GLubyte* )malloc(VI.width*VI.height*4);
|
|
if(pixelData == NULL)
|
|
return;
|
|
glReadPixels( 0, 0, VI.width, VI.height, GL_RGBA, GL_UNSIGNED_BYTE, pixelData );
|
|
#endif // GLES2
|
|
|
|
if (pBuffer->m_size == G_IM_SIZ_32b) {
|
|
u32 *ptr_dst = (u32*)(RDRAM + _address);
|
|
u32 *ptr_src = (u32*)pixelData;
|
|
|
|
for (u32 y = 0; y < VI.height; ++y) {
|
|
for (u32 x = 0; x < VI.width; ++x)
|
|
ptr_dst[x + y*VI.width] = ptr_src[x + (VI.height - y - 1)*VI.width];
|
|
}
|
|
} else {
|
|
u16 *ptr_dst = (u16*)(RDRAM + _address);
|
|
RGBA * ptr_src = (RGBA*)pixelData;
|
|
|
|
for (u32 y = 0; y < VI.height; ++y) {
|
|
for (u32 x = 0; x < VI.width; ++x) {
|
|
const RGBA & c = ptr_src[x + (VI.height - y - 1)*VI.width];
|
|
ptr_dst[(x + y*VI.width)^1] = ((c.r>>3)<<11) | ((c.g>>3)<<6) | ((c.b>>3)<<1) | (c.a == 0 ? 0 : 1);
|
|
}
|
|
}
|
|
}
|
|
pBuffer->m_copiedToRDRAM = true;
|
|
#ifndef GLES2
|
|
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
|
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
|
#else
|
|
free(pixelData);
|
|
#endif
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
|
glEnable(GL_SCISSOR_TEST);
|
|
}
|
|
#endif // GLES2
|
|
|
|
void FrameBuffer_CopyToRDRAM(u32 _address)
|
|
{
|
|
#ifndef GLES2
|
|
g_fbToRDRAM.CopyToRDRAM(_address);
|
|
#endif
|
|
}
|
|
|
|
#ifndef GLES2
|
|
void DepthBufferToRDRAM::Init()
|
|
{
|
|
// generate a framebuffer
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
glGenFramebuffers(1, &m_FBO);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_FBO);
|
|
|
|
m_pColorTexture = textureCache().addFrameBufferTexture();
|
|
m_pColorTexture->format = G_IM_FMT_I;
|
|
m_pColorTexture->clampS = 1;
|
|
m_pColorTexture->clampT = 1;
|
|
m_pColorTexture->frameBufferTexture = TRUE;
|
|
m_pColorTexture->maskS = 0;
|
|
m_pColorTexture->maskT = 0;
|
|
m_pColorTexture->mirrorS = 0;
|
|
m_pColorTexture->mirrorT = 0;
|
|
m_pColorTexture->realWidth = 640;
|
|
m_pColorTexture->realHeight = 480;
|
|
m_pColorTexture->textureBytes = m_pColorTexture->realWidth * m_pColorTexture->realHeight;
|
|
textureCache().addFrameBufferTextureSize(m_pColorTexture->textureBytes);
|
|
|
|
m_pDepthTexture = textureCache().addFrameBufferTexture();
|
|
m_pDepthTexture->format = G_IM_FMT_I;
|
|
m_pDepthTexture->clampS = 1;
|
|
m_pDepthTexture->clampT = 1;
|
|
m_pDepthTexture->frameBufferTexture = TRUE;
|
|
m_pDepthTexture->maskS = 0;
|
|
m_pDepthTexture->maskT = 0;
|
|
m_pDepthTexture->mirrorS = 0;
|
|
m_pDepthTexture->mirrorT = 0;
|
|
m_pDepthTexture->realWidth = 640;
|
|
m_pDepthTexture->realHeight = 480;
|
|
m_pDepthTexture->textureBytes = m_pDepthTexture->realWidth * m_pDepthTexture->realHeight * sizeof(float);
|
|
textureCache().addFrameBufferTextureSize(m_pDepthTexture->textureBytes);
|
|
|
|
glBindTexture( GL_TEXTURE_2D, m_pColorTexture->glName );
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_pColorTexture->realWidth, m_pColorTexture->realHeight, 0, GL_RED, GL_UNSIGNED_BYTE, NULL);
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
|
|
|
|
glBindTexture( GL_TEXTURE_2D, m_pDepthTexture->glName );
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, m_pDepthTexture->realWidth, m_pDepthTexture->realHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_pColorTexture->glName, 0);
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_pDepthTexture->glName, 0);
|
|
// check if everything is OK
|
|
assert(checkFBO());
|
|
assert(!isGLError());
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
|
|
// Generate and initialize Pixel Buffer Objects
|
|
glGenBuffers(1, &m_PBO);
|
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, m_PBO);
|
|
glBufferData(GL_PIXEL_PACK_BUFFER, 640*480*sizeof(float), NULL, GL_DYNAMIC_DRAW);
|
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
|
}
|
|
|
|
void DepthBufferToRDRAM::Destroy() {
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
glDeleteFramebuffers(1, &m_FBO);
|
|
m_FBO = 0;
|
|
if (m_pColorTexture != NULL) {
|
|
textureCache().removeFrameBufferTexture(m_pColorTexture);
|
|
m_pColorTexture = NULL;
|
|
}
|
|
if (m_pDepthTexture != NULL) {
|
|
textureCache().removeFrameBufferTexture(m_pDepthTexture);
|
|
m_pDepthTexture = NULL;
|
|
}
|
|
glDeleteBuffers(1, &m_PBO);
|
|
m_PBO = 0;
|
|
}
|
|
|
|
bool DepthBufferToRDRAM::CopyToRDRAM( u32 _address) {
|
|
if (VI.width == 0) // H width is zero. Don't copy
|
|
return false;
|
|
if (m_lastDList == RSP.DList) // Already read;
|
|
return false;
|
|
FrameBuffer *pBuffer = frameBufferList().findBuffer(_address);
|
|
if (pBuffer == NULL || pBuffer->m_pDepthBuffer == NULL || !pBuffer->m_pDepthBuffer->m_cleared)
|
|
return false;
|
|
|
|
m_lastDList = RSP.DList;
|
|
DepthBuffer * pDepthBuffer = pBuffer->m_pDepthBuffer;
|
|
const u32 address = pDepthBuffer->m_address;
|
|
if (config.video.multisampling == 0)
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, pBuffer->m_FBO);
|
|
else {
|
|
pDepthBuffer->resolveDepthBufferTexture(pBuffer);
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, pBuffer->m_resolveFBO);
|
|
}
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_FBO);
|
|
glDisable(GL_SCISSOR_TEST);
|
|
glBlitFramebuffer(
|
|
0, 0, video().getWidth(), video().getHeight(),
|
|
0, 0, pBuffer->m_width, pBuffer->m_height,
|
|
GL_DEPTH_BUFFER_BIT, GL_NEAREST
|
|
);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBufferList().getCurrent()->m_FBO);
|
|
|
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, m_PBO);
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_FBO);
|
|
glReadPixels(0, 0, VI.width, VI.height, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
|
|
|
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, m_PBO);
|
|
GLubyte* pixelData = (GLubyte*)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
|
|
if(pixelData == NULL) {
|
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
|
return false;
|
|
}
|
|
|
|
f32 * ptr_src = (f32*)pixelData;
|
|
u16 *ptr_dst = (u16*)(RDRAM + address);
|
|
const f32 scale = gSP.viewport.vscale[2] * 32768.0f;
|
|
const f32 trans = gSP.viewport.vtrans[2] * 32768.0f;
|
|
const u16 * const zLUT = depthBufferList().getZLUT();
|
|
|
|
for (u32 y = 0; y < VI.height; ++y) {
|
|
for (u32 x = 0; x < VI.width; ++x) {
|
|
f32 z = ptr_src[x + (VI.height - y - 1)*VI.width];
|
|
if (z == 1.0f)
|
|
ptr_dst[(x + y*VI.width) ^ 1] = zLUT[0x3FFFF];
|
|
else {
|
|
z = z*2.0f - 1.0f;
|
|
z = (z*scale + trans) * 8.0f;
|
|
const u32 idx = min(0x3FFFFU, u32(floorf(z + 0.5f)));
|
|
ptr_dst[(x + y*VI.width) ^ 1] = zLUT[idx];
|
|
}
|
|
}
|
|
}
|
|
|
|
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
|
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
|
glEnable(GL_SCISSOR_TEST);
|
|
return true;
|
|
}
|
|
#endif // GLES2
|
|
|
|
bool FrameBuffer_CopyDepthBuffer( u32 address ) {
|
|
#ifndef GLES2
|
|
return g_dbToRDRAM.CopyToRDRAM(address);
|
|
#endif
|
|
}
|
|
|
|
void RDRAMtoFrameBuffer::Init()
|
|
{
|
|
m_pTexture = textureCache().addFrameBufferTexture();
|
|
m_pTexture->format = G_IM_FMT_RGBA;
|
|
m_pTexture->clampS = 1;
|
|
m_pTexture->clampT = 1;
|
|
m_pTexture->frameBufferTexture = TRUE;
|
|
m_pTexture->maskS = 0;
|
|
m_pTexture->maskT = 0;
|
|
m_pTexture->mirrorS = 0;
|
|
m_pTexture->mirrorT = 0;
|
|
m_pTexture->realWidth = 1024;
|
|
m_pTexture->realHeight = 512;
|
|
m_pTexture->textureBytes = m_pTexture->realWidth * m_pTexture->realHeight * 4;
|
|
textureCache().addFrameBufferTextureSize(m_pTexture->textureBytes);
|
|
glBindTexture( GL_TEXTURE_2D, m_pTexture->glName );
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_pTexture->realWidth, m_pTexture->realHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
// Generate Pixel Buffer Object. Initialize it later
|
|
#ifndef GLES2
|
|
glGenBuffers(1, &m_PBO);
|
|
#endif
|
|
}
|
|
|
|
void RDRAMtoFrameBuffer::Destroy()
|
|
{
|
|
if (m_pTexture != NULL) {
|
|
textureCache().removeFrameBufferTexture(m_pTexture);
|
|
m_pTexture = NULL;
|
|
}
|
|
#ifndef GLES2
|
|
glDeleteBuffers(1, &m_PBO);
|
|
m_PBO = 0;
|
|
#endif
|
|
}
|
|
|
|
void RDRAMtoFrameBuffer::CopyFromRDRAM( u32 _address, bool _bUseAlpha)
|
|
{
|
|
FrameBuffer *pBuffer = frameBufferList().findBuffer(_address);
|
|
if (pBuffer == NULL || pBuffer->m_size < G_IM_SIZ_16b)
|
|
return;
|
|
if (pBuffer->m_startAddress == _address && gDP.colorImage.changed != 0)
|
|
return;
|
|
|
|
const u32 address = pBuffer->m_startAddress;
|
|
const u32 width = pBuffer->m_width;
|
|
const u32 height = pBuffer->m_startAddress == _address ? VI.real_height : pBuffer->m_height;
|
|
m_pTexture->width = width;
|
|
m_pTexture->height = height;
|
|
const u32 dataSize = width*height*4;
|
|
#ifndef GLES2
|
|
PBOBinder binder(m_PBO);
|
|
glBufferData(GL_PIXEL_UNPACK_BUFFER, dataSize, NULL, GL_DYNAMIC_DRAW);
|
|
GLubyte* ptr = (GLubyte*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
|
|
#else
|
|
m_PBO = (GLubyte*)malloc(dataSize);
|
|
GLubyte* ptr = m_PBO;
|
|
PBOBinder binder(m_PBO);
|
|
#endif // GLES2
|
|
if (ptr == NULL)
|
|
return;
|
|
|
|
u8 * image = RDRAM + address;
|
|
u32 * dst = (u32*)ptr;
|
|
|
|
u32 empty = 0;
|
|
u32 r, g, b,a, idx;
|
|
if (pBuffer->m_size == G_IM_SIZ_16b) {
|
|
u16 * src = (u16*)image;
|
|
u16 col;
|
|
const u32 bound = (RDRAMSize + 1 - address) >> 1;
|
|
for (u32 y = 0; y < height; y++)
|
|
{
|
|
for (u32 x = 0; x < width; x++)
|
|
{
|
|
idx = (x + (height - y - 1)*width)^1;
|
|
if (idx >= bound)
|
|
break;
|
|
col = src[idx];
|
|
if (_bUseAlpha)
|
|
src[idx] = 0;
|
|
empty |= col;
|
|
r = ((col >> 11)&31)<<3;
|
|
g = ((col >> 6)&31)<<3;
|
|
b = ((col >> 1)&31)<<3;
|
|
a = (col&1) > 0 ? 0xff : 0;
|
|
dst[x + y*width] = (a << 24) | (b << 16) | (g << 8) | r;
|
|
}
|
|
}
|
|
} else {
|
|
// 32 bit
|
|
u32 * src = (u32*)image;
|
|
u32 col;
|
|
const u32 bound = (RDRAMSize + 1 - address) >> 2;
|
|
for (u32 y=0; y < height; y++)
|
|
{
|
|
for (u32 x=0; x < width; x++)
|
|
{
|
|
idx = x + (height - y - 1)*width;
|
|
if (idx >= bound)
|
|
break;
|
|
col = src[idx];
|
|
if (_bUseAlpha)
|
|
src[idx] = 0;
|
|
empty |= col;
|
|
r = (col >> 24) & 0xff;
|
|
g = (col >> 16) & 0xff;
|
|
b = (col >> 8) & 0xff;
|
|
a = col & 0xff;
|
|
dst[x + y*width] = (a<<24)|(b<<16)|(g<<8)|r;
|
|
}
|
|
}
|
|
}
|
|
#ifndef GLES2
|
|
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); // release the mapped buffer
|
|
#endif
|
|
if (empty == 0)
|
|
return;
|
|
|
|
glBindTexture(GL_TEXTURE_2D, m_pTexture->glName);
|
|
#ifndef GLES2
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
|
#else
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, m_PBO);
|
|
#endif
|
|
|
|
if (_bUseAlpha) {
|
|
CombinerInfo::get().setCombine(EncodeCombineMode(0, 0, 0, TEXEL0, 0, 0, 0, TEXEL0, 0, 0, 0, TEXEL0, 0, 0, 0, TEXEL0));
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
} else {
|
|
CombinerInfo::get().setCombine(EncodeCombineMode(0, 0, 0, TEXEL0, 0, 0, 0, 1, 0, 0, 0, TEXEL0, 0, 0, 0, 1));
|
|
glDisable(GL_BLEND);
|
|
}
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDisable( GL_CULL_FACE );
|
|
const u32 gspChanged = gSP.changed & CHANGED_CPU_FB_WRITE;
|
|
gSP.changed = gDP.changed = 0;
|
|
|
|
m_pTexture->scaleS = 1.0f / (float)m_pTexture->realWidth;
|
|
m_pTexture->scaleT = 1.0f / (float)m_pTexture->realHeight;
|
|
m_pTexture->shiftScaleS = 1.0f;
|
|
m_pTexture->shiftScaleT = 1.0f;
|
|
m_pTexture->offsetS = 0;
|
|
m_pTexture->offsetT = (float)m_pTexture->height;
|
|
textureCache().activateTexture(0, m_pTexture);
|
|
|
|
gDPTile tile0;
|
|
tile0.fuls = tile0.fult = 0.0f;
|
|
gDPTile * pTile0 = gSP.textureTile[0];
|
|
gSP.textureTile[0] = &tile0;
|
|
|
|
OGLRender::TexturedRectParams params(0.0f, 0.0f, (float)width, (float)height, 0.0f, 0.0f, width - 1.0f, height - 1.0f, false);
|
|
video().getRender().drawTexturedRect(params);
|
|
|
|
gSP.textureTile[0] = pTile0;
|
|
|
|
gSP.changed |= gspChanged | CHANGED_TEXTURE | CHANGED_VIEWPORT;
|
|
gDP.changed |= CHANGED_COMBINE;
|
|
}
|
|
|
|
void FrameBuffer_CopyFromRDRAM( u32 address, bool bUseAlpha )
|
|
{
|
|
g_RDRAMtoFB.CopyFromRDRAM(address, bUseAlpha);
|
|
}
|