#ifdef OS_MAC_OS_X #include #else #include #endif #include #include "OpenGL.h" #include "Combiner.h" #include "FrameBuffer.h" #include "DepthBuffer.h" #include "VI.h" #include "Config.h" #include "Debug.h" const GLuint ZlutImageUnit = 0; const GLuint TlutImageUnit = 1; const GLuint depthImageUnit = 2; DepthBuffer::DepthBuffer() : m_address(0), m_width(0), m_uly(0), m_lry(0), m_depthImageFBO(0), m_pDepthImageTexture(NULL), m_pDepthBufferTexture(NULL), m_cleared(false), m_pResolveDepthBufferTexture(NULL), m_resolved(false), m_pDepthBufferCopyTexture(NULL), m_copied(false) { glGenFramebuffers(1, &m_copyFBO); if (video().getRender().isImageTexturesSupported() && config.frameBufferEmulation.N64DepthCompare != 0) glGenFramebuffers(1, &m_depthImageFBO); } DepthBuffer::DepthBuffer(DepthBuffer && _other) : m_address(_other.m_address), m_width(_other.m_width), m_uly(_other.m_uly), m_lry(_other.m_lry), m_depthImageFBO(_other.m_depthImageFBO), m_pDepthImageTexture(_other.m_pDepthImageTexture), m_pDepthBufferTexture(_other.m_pDepthBufferTexture), m_cleared(_other.m_cleared), m_pResolveDepthBufferTexture(_other.m_pResolveDepthBufferTexture), m_resolved(_other.m_resolved), m_pDepthBufferCopyTexture(_other.m_pDepthBufferCopyTexture), m_copied(m_copied) { _other.m_depthImageFBO = 0; _other.m_pDepthImageTexture = NULL; _other.m_pDepthBufferTexture = NULL; _other.m_pResolveDepthBufferTexture = NULL; _other.m_resolved = false; _other.m_pDepthBufferCopyTexture = NULL; _other.m_copied = false; } DepthBuffer::~DepthBuffer() { if (m_depthImageFBO != 0) glDeleteFramebuffers(1, &m_depthImageFBO); if (m_pDepthImageTexture != NULL) textureCache().removeFrameBufferTexture(m_pDepthImageTexture); if (m_pDepthBufferTexture != NULL) textureCache().removeFrameBufferTexture(m_pDepthBufferTexture); if (m_pResolveDepthBufferTexture != NULL) textureCache().removeFrameBufferTexture(m_pResolveDepthBufferTexture); if (m_copyFBO != 0) glDeleteFramebuffers(1, &m_copyFBO); if (m_pDepthBufferCopyTexture != NULL) textureCache().removeFrameBufferTexture(m_pDepthBufferCopyTexture); } void DepthBuffer::initDepthImageTexture(FrameBuffer * _pBuffer) { #ifdef GL_IMAGE_TEXTURES_SUPPORT if (!video().getRender().isImageTexturesSupported() || config.frameBufferEmulation.N64DepthCompare == 0 || m_pDepthImageTexture != NULL) return; m_pDepthImageTexture = textureCache().addFrameBufferTexture(); m_pDepthImageTexture->width = (u32)(_pBuffer->m_pTexture->width); m_pDepthImageTexture->height = (u32)(_pBuffer->m_pTexture->height); m_pDepthImageTexture->format = 0; m_pDepthImageTexture->size = 2; m_pDepthImageTexture->clampS = 1; m_pDepthImageTexture->clampT = 1; m_pDepthImageTexture->address = _pBuffer->m_startAddress; m_pDepthImageTexture->clampWidth = _pBuffer->m_width; m_pDepthImageTexture->clampHeight = _pBuffer->m_height; m_pDepthImageTexture->frameBufferTexture = CachedTexture::fbOneSample; m_pDepthImageTexture->maskS = 0; m_pDepthImageTexture->maskT = 0; m_pDepthImageTexture->mirrorS = 0; m_pDepthImageTexture->mirrorT = 0; m_pDepthImageTexture->realWidth = m_pDepthImageTexture->width; m_pDepthImageTexture->realHeight = m_pDepthImageTexture->height; m_pDepthImageTexture->textureBytes = m_pDepthImageTexture->realWidth * m_pDepthImageTexture->realHeight * fboFormats.depthImageFormatBytes; textureCache().addFrameBufferTextureSize(m_pDepthImageTexture->textureBytes); glBindTexture(GL_TEXTURE_2D, m_pDepthImageTexture->glName); glTexImage2D(GL_TEXTURE_2D, 0, fboFormats.depthImageInternalFormat, m_pDepthImageTexture->realWidth, m_pDepthImageTexture->realHeight, 0, fboFormats.depthImageFormat, fboFormats.depthImageType, 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); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_depthImageFBO); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_pDepthImageTexture->glName, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _pBuffer->m_FBO); depthBufferList().clearBuffer(0, VI.height); #endif // GL_IMAGE_TEXTURES_SUPPORT } void DepthBuffer::_initDepthBufferTexture(FrameBuffer * _pBuffer, CachedTexture * _pTexture, bool _multisample) { if (_pBuffer != NULL) { _pTexture->width = (u32)(_pBuffer->m_pTexture->width); _pTexture->height = (u32)(_pBuffer->m_pTexture->height); _pTexture->address = _pBuffer->m_startAddress; _pTexture->clampWidth = _pBuffer->m_width; _pTexture->clampHeight = _pBuffer->m_height; } else { if (config.frameBufferEmulation.nativeResFactor == 0) { _pTexture->width = video().getWidth(); _pTexture->height = video().getHeight(); } else { _pTexture->width = VI.width * config.frameBufferEmulation.nativeResFactor; _pTexture->height = VI.height * config.frameBufferEmulation.nativeResFactor; } _pTexture->address = gDP.depthImageAddress; _pTexture->clampWidth = VI.width; _pTexture->clampHeight = VI.height; } _pTexture->format = 0; _pTexture->size = 2; _pTexture->clampS = 1; _pTexture->clampT = 1; _pTexture->frameBufferTexture = CachedTexture::fbOneSample; _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 * fboFormats.depthFormatBytes; textureCache().addFrameBufferTextureSize(_pTexture->textureBytes); #ifdef GL_MULTISAMPLING_SUPPORT if (_multisample) { glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _pTexture->glName); #if defined(GLES3_1) glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, config.video.multisampling, GL_DEPTH_COMPONENT, _pTexture->realWidth, _pTexture->realHeight, false); #else glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, config.video.multisampling, GL_DEPTH_COMPONENT, _pTexture->realWidth, _pTexture->realHeight, false); #endif _pTexture->frameBufferTexture = CachedTexture::fbMultiSample; } else #endif // GL_MULTISAMPLING_SUPPORT { glBindTexture(GL_TEXTURE_2D, _pTexture->glName); glTexImage2D(GL_TEXTURE_2D, 0, fboFormats.depthInternalFormat, _pTexture->realWidth, _pTexture->realHeight, 0, GL_DEPTH_COMPONENT, fboFormats.depthType, 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); } void DepthBuffer::setDepthAttachment(GLenum _target) { #ifdef GL_MULTISAMPLING_SUPPORT if (config.video.multisampling != 0) glFramebufferTexture2D(_target, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, m_pDepthBufferTexture->glName, 0); else #endif glFramebufferTexture2D(_target, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_pDepthBufferTexture->glName, 0); m_copied = false; m_resolved = false; } void DepthBuffer::initDepthBufferTexture(FrameBuffer * _pBuffer) { if (m_pDepthBufferTexture == NULL) { m_pDepthBufferTexture = textureCache().addFrameBufferTexture(); _initDepthBufferTexture(_pBuffer, m_pDepthBufferTexture, config.video.multisampling != 0); } #ifdef GL_MULTISAMPLING_SUPPORT if (config.video.multisampling != 0 && m_pResolveDepthBufferTexture == NULL) { m_pResolveDepthBufferTexture = textureCache().addFrameBufferTexture(); _initDepthBufferTexture(_pBuffer, m_pResolveDepthBufferTexture, false); } #endif } CachedTexture * DepthBuffer::resolveDepthBufferTexture(FrameBuffer * _pBuffer) { #ifdef GL_MULTISAMPLING_SUPPORT if (config.video.multisampling == 0) return m_pDepthBufferTexture; if (m_resolved) return m_pResolveDepthBufferTexture; glBindFramebuffer(GL_READ_FRAMEBUFFER, _pBuffer->m_FBO); glReadBuffer(GL_COLOR_ATTACHMENT0); GLuint attachment = GL_COLOR_ATTACHMENT0; glDrawBuffers(1, &attachment); assert(checkFBO()); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _pBuffer->m_resolveFBO); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_pResolveDepthBufferTexture->glName, 0); assert(checkFBO()); glDisable(GL_SCISSOR_TEST); glBlitFramebuffer( 0, 0, m_pDepthBufferTexture->realWidth, m_pDepthBufferTexture->realHeight, 0, 0, m_pResolveDepthBufferTexture->realWidth, m_pResolveDepthBufferTexture->realHeight, GL_DEPTH_BUFFER_BIT, GL_NEAREST ); glEnable(GL_SCISSOR_TEST); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _pBuffer->m_FBO); m_resolved = true; return m_pResolveDepthBufferTexture; #else return m_pDepthBufferTexture; #endif } #ifndef GLES2 CachedTexture * DepthBuffer::copyDepthBufferTexture(FrameBuffer * _pBuffer) { if (m_copied) return m_pDepthBufferCopyTexture; if (m_pDepthBufferCopyTexture == NULL) { m_pDepthBufferCopyTexture = textureCache().addFrameBufferTexture(); _initDepthBufferTexture(_pBuffer, m_pDepthBufferCopyTexture, false); } glBindFramebuffer(GL_READ_FRAMEBUFFER, _pBuffer->m_FBO); glReadBuffer(GL_COLOR_ATTACHMENT0); assert(checkFBO()); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_copyFBO); #ifdef GL_MULTISAMPLING_SUPPORT GLenum textarget = config.video.multisampling != 0 ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D; #else GLenum textarget = GL_TEXTURE_2D; #endif glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textarget, _pBuffer->m_pTexture->frameBufferTexture == CachedTexture::fbMultiSample ? _pBuffer->m_pResolveTexture->glName : _pBuffer->m_pTexture->glName, 0); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_pDepthBufferCopyTexture->glName, 0); assert(checkFBO()); glDisable(GL_SCISSOR_TEST); glBlitFramebuffer( 0, 0, m_pDepthBufferTexture->realWidth, m_pDepthBufferTexture->realHeight, 0, 0, m_pDepthBufferTexture->realWidth, m_pDepthBufferTexture->realHeight, GL_DEPTH_BUFFER_BIT, GL_NEAREST ); glEnable(GL_SCISSOR_TEST); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _pBuffer->m_FBO); m_copied = true; return m_pDepthBufferCopyTexture; } #endif void DepthBuffer::activateDepthBufferTexture(FrameBuffer * _pBuffer) { textureCache().activateTexture(0, resolveDepthBufferTexture(_pBuffer)); } void DepthBuffer::bindDepthImageTexture() { #ifdef GL_IMAGE_TEXTURES_SUPPORT glBindImageTexture(depthImageUnit, m_pDepthImageTexture->glName, 0, GL_FALSE, 0, GL_READ_WRITE, fboFormats.depthImageInternalFormat); #endif } DepthBufferList::DepthBufferList() : m_pCurrent(NULL), m_pzLUT(NULL) { m_pzLUT = new u16[0x40000]; for (int i = 0; i<0x40000; i++) { u32 exponent = 0; u32 testbit = 1 << 17; while ((i & testbit) && (exponent < 7)) { exponent++; testbit = 1 << (17 - exponent); } const u32 mantissa = (i >> (6 - (6 < exponent ? 6 : exponent))) & 0x7ff; m_pzLUT[i] = (u16)(((exponent << 11) | mantissa) << 2); } } DepthBufferList::~DepthBufferList() { delete[] m_pzLUT; m_pzLUT = NULL; m_list.clear(); } DepthBufferList & DepthBufferList::get() { static DepthBufferList depthBufferList; return depthBufferList; } void DepthBufferList::init() { m_pCurrent = NULL; } void DepthBufferList::destroy() { m_pCurrent = NULL; m_list.clear(); } void DepthBufferList::setNotCleared() { for (DepthBuffers::iterator iter = m_list.begin(); iter != m_list.end(); ++iter) iter->m_cleared = false; } DepthBuffer * DepthBufferList::findBuffer(u32 _address) { for (DepthBuffers::iterator iter = m_list.begin(); iter != m_list.end(); ++iter) if (iter->m_address == _address) return &(*iter); return NULL; } void DepthBufferList::removeBuffer(u32 _address ) { for (DepthBuffers::iterator iter = m_list.begin(); iter != m_list.end(); ++iter) if (iter->m_address == _address) { frameBufferList().clearDepthBuffer(&(*iter)); m_list.erase(iter); return; } } void DepthBufferList::saveBuffer(u32 _address) { if (!config.frameBufferEmulation.enable) return; FrameBuffer * pFrameBuffer = frameBufferList().findBuffer(_address); if (pFrameBuffer != NULL) pFrameBuffer->m_isDepthBuffer = true; if (m_pCurrent == NULL || m_pCurrent->m_address != _address) m_pCurrent = findBuffer(_address); if (m_pCurrent != NULL && pFrameBuffer != NULL && m_pCurrent->m_width != pFrameBuffer->m_width) { removeBuffer(_address); m_pCurrent = NULL; } if (m_pCurrent == NULL) { m_list.emplace_front(); DepthBuffer & buffer = m_list.front(); buffer.m_address = _address; buffer.m_width = pFrameBuffer != NULL ? pFrameBuffer->m_width : VI.width; buffer.initDepthBufferTexture(pFrameBuffer); m_pCurrent = &buffer; } frameBufferList().attachDepthBuffer(); #ifdef DEBUG DebugMsg( DEBUG_HIGH | DEBUG_HANDLED, "DepthBuffer_SetBuffer( 0x%08X ); color buffer is 0x%08X\n", address, ( pFrameBuffer != NULL && pFrameBuffer->m_FBO > 0) ? pFrameBuffer->m_startAddress : 0 ); #endif } void DepthBufferList::clearBuffer(u32 _uly, u32 _lry) { if (m_pCurrent == NULL) return; m_pCurrent->m_cleared = true; m_pCurrent->m_uly = _uly; m_pCurrent->m_lry = _lry; #ifdef GL_IMAGE_TEXTURES_SUPPORT if (m_pCurrent->m_depthImageFBO == 0 || !video().getRender().isImageTexturesSupported() || config.frameBufferEmulation.N64DepthCompare == 0) return; float color[4] = {1.0f, 1.0f, 0.0f, 1.0f}; glBindImageTexture(depthImageUnit, 0, 0, GL_FALSE, 0, GL_READ_WRITE, fboFormats.depthImageInternalFormat); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_pCurrent->m_depthImageFBO); const u32 cycleType = gDP.otherMode.cycleType; gDP.otherMode.cycleType = G_CYC_FILL; video().getRender().drawRect(0,0,VI.width, VI.height, color); gDP.otherMode.cycleType = cycleType; glBindImageTexture(depthImageUnit, m_pCurrent->m_pDepthImageTexture->glName, 0, GL_FALSE, 0, GL_READ_WRITE, fboFormats.depthImageInternalFormat); frameBufferList().setCurrentDrawBuffer(); #endif // GL_IMAGE_TEXTURES_SUPPORT } void DepthBuffer_Init() { depthBufferList().init(); } void DepthBuffer_Destroy() { depthBufferList().destroy(); }