2013-06-01 13:10:30 +00:00
|
|
|
#include <assert.h>
|
2015-01-28 13:58:55 +00:00
|
|
|
#include <math.h>
|
2016-02-03 15:17:53 +00:00
|
|
|
#include <algorithm>
|
|
|
|
#include <vector>
|
2013-04-05 06:13:26 +00:00
|
|
|
#include "FrameBuffer.h"
|
2013-06-01 13:10:30 +00:00
|
|
|
#include "DepthBuffer.h"
|
2013-11-27 10:08:48 +00:00
|
|
|
#include "N64.h"
|
2013-04-05 06:13:26 +00:00
|
|
|
#include "RSP.h"
|
|
|
|
#include "RDP.h"
|
2013-08-10 11:12:17 +00:00
|
|
|
#include "VI.h"
|
2013-04-05 06:13:26 +00:00
|
|
|
#include "Textures.h"
|
|
|
|
#include "Combiner.h"
|
|
|
|
#include "Types.h"
|
2014-04-05 02:57:00 +00:00
|
|
|
#include "Config.h"
|
2017-06-16 08:36:07 +00:00
|
|
|
#include "Debugger.h"
|
2017-05-25 08:54:30 +00:00
|
|
|
#include "DebugDump.h"
|
2015-01-30 13:59:52 +00:00
|
|
|
#include "PostProcessor.h"
|
2015-11-23 12:36:03 +00:00
|
|
|
#include "FrameBufferInfo.h"
|
2016-05-24 05:18:56 +00:00
|
|
|
#include "Log.h"
|
2019-03-15 10:04:33 +00:00
|
|
|
#include "MemoryStatus.h"
|
2016-05-24 05:18:56 +00:00
|
|
|
|
2016-05-28 15:37:20 +00:00
|
|
|
#include "BufferCopy/ColorBufferToRDRAM.h"
|
|
|
|
#include "BufferCopy/DepthBufferToRDRAM.h"
|
|
|
|
#include "BufferCopy/RDRAMtoColorBuffer.h"
|
2013-06-01 13:10:30 +00:00
|
|
|
|
2017-01-01 17:09:19 +00:00
|
|
|
#include <Graphics/Context.h>
|
|
|
|
#include <Graphics/Parameters.h>
|
2021-04-18 00:45:47 +00:00
|
|
|
#include <Graphics/ColorBufferReader.h>
|
2017-01-15 07:57:25 +00:00
|
|
|
#include "DisplayWindow.h"
|
2017-01-01 17:09:19 +00:00
|
|
|
|
2014-09-20 17:34:07 +00:00
|
|
|
using namespace std;
|
2017-01-20 11:47:19 +00:00
|
|
|
using namespace graphics;
|
2014-09-20 17:34:07 +00:00
|
|
|
|
2017-11-18 15:07:27 +00:00
|
|
|
FrameBuffer::FrameBuffer()
|
2020-11-14 09:54:28 +00:00
|
|
|
: m_copyFBO(ObjectHandle::defaultFramebuffer)
|
2021-04-18 00:45:47 +00:00
|
|
|
, m_ColorBufferFBO(0)
|
|
|
|
, m_pColorBufferTexture(nullptr)
|
2020-11-14 09:54:28 +00:00
|
|
|
{
|
2019-11-29 22:09:09 +00:00
|
|
|
m_pTexture = textureCache().addFrameBufferTexture(config.video.multisampling != 0 ?
|
|
|
|
textureTarget::TEXTURE_2D_MULTISAMPLE : textureTarget::TEXTURE_2D);
|
2017-01-20 11:47:19 +00:00
|
|
|
m_FBO = gfxContext.createFramebuffer();
|
2019-06-27 09:21:51 +00:00
|
|
|
|
|
|
|
if (config.frameBufferEmulation.copyDepthToMainDepthBuffer != 0)
|
|
|
|
m_depthFBO = gfxContext.createFramebuffer();
|
2013-04-05 06:13:26 +00:00
|
|
|
}
|
|
|
|
|
2014-09-08 11:01:22 +00:00
|
|
|
FrameBuffer::~FrameBuffer()
|
2013-04-05 06:13:26 +00:00
|
|
|
{
|
2017-01-20 11:47:19 +00:00
|
|
|
gfxContext.deleteFramebuffer(m_FBO);
|
2019-06-27 09:21:51 +00:00
|
|
|
gfxContext.deleteFramebuffer(m_depthFBO);
|
2017-01-20 11:47:19 +00:00
|
|
|
gfxContext.deleteFramebuffer(m_resolveFBO);
|
|
|
|
gfxContext.deleteFramebuffer(m_SubFBO);
|
2018-04-16 19:13:18 +00:00
|
|
|
gfxContext.deleteFramebuffer(m_copyFBO);
|
2017-01-09 04:21:06 +00:00
|
|
|
|
2017-01-02 14:00:49 +00:00
|
|
|
textureCache().removeFrameBufferTexture(m_pTexture);
|
2019-06-27 09:21:51 +00:00
|
|
|
textureCache().removeFrameBufferTexture(m_pDepthTexture);
|
2017-01-02 14:00:49 +00:00
|
|
|
textureCache().removeFrameBufferTexture(m_pResolveTexture);
|
|
|
|
textureCache().removeFrameBufferTexture(m_pSubTexture);
|
2018-04-16 19:13:18 +00:00
|
|
|
textureCache().removeFrameBufferTexture(m_pFrameBufferCopyTexture);
|
2021-04-18 00:45:47 +00:00
|
|
|
|
|
|
|
_destroyColorFBTexure();
|
2015-02-09 12:27:39 +00:00
|
|
|
}
|
|
|
|
|
2018-05-05 07:45:18 +00:00
|
|
|
static
|
|
|
|
void _initFrameBufferTexture(u32 _address, u16 _width, u16 _height, f32 _scale, u16 _format, u16 _size, CachedTexture *_pTexture)
|
2015-02-09 12:27:39 +00:00
|
|
|
{
|
2017-01-20 11:47:19 +00:00
|
|
|
const FramebufferTextureFormats & fbTexFormats = gfxContext.getFramebufferTextureFormats();
|
2017-01-14 12:26:02 +00:00
|
|
|
|
2020-11-14 09:54:28 +00:00
|
|
|
_pTexture->width = static_cast<u16>(static_cast<u32>(static_cast<f32>(_width) * _scale));
|
|
|
|
_pTexture->height = static_cast<u16>(static_cast<u32>(static_cast<f32>(_height) * _scale));
|
2015-02-09 12:27:39 +00:00
|
|
|
_pTexture->format = _format;
|
|
|
|
_pTexture->size = _size;
|
|
|
|
_pTexture->clampS = 1;
|
|
|
|
_pTexture->clampT = 1;
|
2018-05-05 07:45:18 +00:00
|
|
|
_pTexture->address = _address;
|
2016-05-08 09:58:19 +00:00
|
|
|
_pTexture->clampWidth = _width;
|
|
|
|
_pTexture->clampHeight = _height;
|
2015-05-06 13:47:12 +00:00
|
|
|
_pTexture->frameBufferTexture = CachedTexture::fbOneSample;
|
2015-02-09 12:27:39 +00:00
|
|
|
_pTexture->maskS = 0;
|
|
|
|
_pTexture->maskT = 0;
|
|
|
|
_pTexture->mirrorS = 0;
|
|
|
|
_pTexture->mirrorT = 0;
|
2019-05-08 14:04:24 +00:00
|
|
|
_pTexture->textureBytes = _pTexture->width * _pTexture->height;
|
2020-04-18 16:20:17 +00:00
|
|
|
_pTexture->hdRatioS = _scale;
|
|
|
|
_pTexture->hdRatioT = _scale;
|
2015-02-09 12:27:39 +00:00
|
|
|
if (_size > G_IM_SIZ_8b)
|
2017-01-14 12:26:02 +00:00
|
|
|
_pTexture->textureBytes *= fbTexFormats.colorFormatBytes;
|
2015-06-02 12:57:24 +00:00
|
|
|
else
|
2017-01-14 12:26:02 +00:00
|
|
|
_pTexture->textureBytes *= fbTexFormats.monochromeFormatBytes;
|
2015-02-09 12:27:39 +00:00
|
|
|
}
|
|
|
|
|
2018-05-05 07:45:18 +00:00
|
|
|
void FrameBuffer::_initTexture(u16 _width, u16 _height, u16 _format, u16 _size, CachedTexture *_pTexture)
|
|
|
|
{
|
|
|
|
_initFrameBufferTexture(m_startAddress, _width, _height, m_scale, _format, _size, _pTexture);
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
void _setAndAttachBufferTexture(ObjectHandle _fbo, CachedTexture *_pTexture, u32 _t, bool _multisampling)
|
2015-02-09 12:27:39 +00:00
|
|
|
{
|
2017-02-12 09:10:58 +00:00
|
|
|
const FramebufferTextureFormats & fbTexFormat = gfxContext.getFramebufferTextureFormats();
|
|
|
|
Context::InitTextureParams initParams;
|
|
|
|
initParams.handle = _pTexture->name;
|
|
|
|
initParams.textureUnitIndex = textureIndices::Tex[_t];
|
|
|
|
if (_multisampling)
|
|
|
|
initParams.msaaLevel = config.video.multisampling;
|
2019-05-08 14:04:24 +00:00
|
|
|
initParams.width = _pTexture->width;
|
|
|
|
initParams.height = _pTexture->height;
|
2017-02-12 09:10:58 +00:00
|
|
|
if (_pTexture->size > G_IM_SIZ_8b) {
|
|
|
|
initParams.internalFormat = fbTexFormat.colorInternalFormat;
|
|
|
|
initParams.format = fbTexFormat.colorFormat;
|
|
|
|
initParams.dataType = fbTexFormat.colorType;
|
|
|
|
} else {
|
|
|
|
initParams.internalFormat = fbTexFormat.monochromeInternalFormat;
|
|
|
|
initParams.format = fbTexFormat.monochromeFormat;
|
|
|
|
initParams.dataType = fbTexFormat.monochromeType;
|
2017-01-01 17:09:19 +00:00
|
|
|
}
|
2017-02-12 09:10:58 +00:00
|
|
|
gfxContext.init2DTexture(initParams);
|
|
|
|
|
2017-02-12 09:05:02 +00:00
|
|
|
if (!_multisampling) {
|
2017-02-12 09:10:58 +00:00
|
|
|
Context::TexParameters texParams;
|
|
|
|
texParams.handle = _pTexture->name;
|
|
|
|
texParams.target = textureTarget::TEXTURE_2D;
|
|
|
|
texParams.textureUnitIndex = textureIndices::Tex[_t];
|
|
|
|
texParams.minFilter = textureParameters::FILTER_NEAREST;
|
|
|
|
texParams.magFilter = textureParameters::FILTER_NEAREST;
|
|
|
|
gfxContext.setTextureParameters(texParams);
|
2017-01-02 14:00:49 +00:00
|
|
|
}
|
2017-02-12 09:10:58 +00:00
|
|
|
|
|
|
|
Context::FrameBufferRenderTarget bufTarget;
|
|
|
|
bufTarget.bufferHandle = _fbo;
|
|
|
|
bufTarget.bufferTarget = bufferTarget::FRAMEBUFFER;
|
|
|
|
bufTarget.attachment = bufferAttachment::COLOR_ATTACHMENT0;
|
|
|
|
bufTarget.textureTarget = _multisampling ? textureTarget::TEXTURE_2D_MULTISAMPLE : textureTarget::TEXTURE_2D;
|
|
|
|
bufTarget.textureHandle = _pTexture->name;
|
|
|
|
gfxContext.addFrameBufferRenderTarget(bufTarget);
|
2017-01-22 13:43:05 +00:00
|
|
|
assert(!gfxContext.isFramebufferError());
|
2015-02-09 12:27:39 +00:00
|
|
|
}
|
|
|
|
|
2018-05-05 07:45:18 +00:00
|
|
|
void FrameBuffer::_setAndAttachTexture(ObjectHandle _fbo, CachedTexture *_pTexture, u32 _t, bool _multisampling)
|
|
|
|
{
|
|
|
|
_setAndAttachBufferTexture(_fbo, _pTexture, _t, _multisampling);
|
|
|
|
}
|
|
|
|
|
2016-02-14 11:25:07 +00:00
|
|
|
bool FrameBuffer::isAuxiliary() const
|
|
|
|
{
|
2022-02-13 12:52:19 +00:00
|
|
|
return m_width != VI.width || m_size < G_IM_SIZ_16b;
|
2016-02-14 11:25:07 +00:00
|
|
|
}
|
|
|
|
|
2017-03-02 15:09:08 +00:00
|
|
|
void FrameBuffer::init(u32 _address, u16 _format, u16 _size, u16 _width, bool _cfb)
|
2015-02-09 12:27:39 +00:00
|
|
|
{
|
|
|
|
m_startAddress = _address;
|
|
|
|
m_width = _width;
|
2017-03-01 16:12:39 +00:00
|
|
|
m_height = _cfb ? VI.height : 1;
|
|
|
|
// m_height = VI.height;
|
2015-02-09 12:27:39 +00:00
|
|
|
m_size = _size;
|
2017-03-18 13:09:10 +00:00
|
|
|
updateEndAddress();
|
2016-02-14 11:25:07 +00:00
|
|
|
if (isAuxiliary() && config.frameBufferEmulation.copyAuxToRDRAM != 0) {
|
2017-03-09 10:55:16 +00:00
|
|
|
m_scale = 1.0f;
|
2017-02-25 06:38:33 +00:00
|
|
|
} else if (config.frameBufferEmulation.nativeResFactor != 0 && config.frameBufferEmulation.enable != 0) {
|
2017-03-09 10:55:16 +00:00
|
|
|
m_scale = static_cast<float>(config.frameBufferEmulation.nativeResFactor);
|
2015-10-31 02:27:34 +00:00
|
|
|
} else {
|
2019-09-05 11:47:45 +00:00
|
|
|
m_scale = std::max(dwnd().getScaleX(), 1.0f);
|
2015-10-31 02:27:34 +00:00
|
|
|
}
|
2015-02-09 12:27:39 +00:00
|
|
|
m_cfb = _cfb;
|
2015-03-17 13:36:17 +00:00
|
|
|
m_cleared = false;
|
2015-08-02 19:01:02 +00:00
|
|
|
m_fingerprint = false;
|
2018-09-25 09:37:57 +00:00
|
|
|
m_swapCount = dwnd().getBuffersSwapCount();
|
2015-02-09 12:27:39 +00:00
|
|
|
|
2017-03-02 15:09:08 +00:00
|
|
|
const u16 maxHeight = VI_GetMaxBufferHeight(_width);
|
|
|
|
_initTexture(_width, maxHeight, _format, _size, m_pTexture);
|
2017-01-01 17:09:19 +00:00
|
|
|
|
|
|
|
if (config.video.multisampling != 0) {
|
2017-01-02 14:00:49 +00:00
|
|
|
_setAndAttachTexture(m_FBO, m_pTexture, 0, true);
|
2017-01-01 17:09:19 +00:00
|
|
|
m_pTexture->frameBufferTexture = CachedTexture::fbMultiSample;
|
|
|
|
|
2019-11-29 22:09:09 +00:00
|
|
|
m_pResolveTexture = textureCache().addFrameBufferTexture(textureTarget::TEXTURE_2D);
|
2017-03-02 15:09:08 +00:00
|
|
|
_initTexture(_width, maxHeight, _format, _size, m_pResolveTexture);
|
2017-01-20 11:47:19 +00:00
|
|
|
m_resolveFBO = gfxContext.createFramebuffer();
|
2017-01-02 14:00:49 +00:00
|
|
|
_setAndAttachTexture(m_resolveFBO, m_pResolveTexture, 0, false);
|
2017-01-22 13:43:05 +00:00
|
|
|
assert(!gfxContext.isFramebufferError());
|
2017-01-01 17:09:19 +00:00
|
|
|
|
2017-01-20 11:47:19 +00:00
|
|
|
gfxContext.bindFramebuffer(bufferTarget::FRAMEBUFFER, m_FBO);
|
2017-01-01 17:09:19 +00:00
|
|
|
} else
|
2017-01-02 14:00:49 +00:00
|
|
|
_setAndAttachTexture(m_FBO, m_pTexture, 0, false);
|
2017-01-01 17:09:19 +00:00
|
|
|
|
2018-09-15 15:46:15 +00:00
|
|
|
// gfxContext.clearColorBuffer(0.0f, 0.0f, 0.0f, 0.0f);
|
2015-02-09 12:27:39 +00:00
|
|
|
}
|
|
|
|
|
2017-03-02 15:09:08 +00:00
|
|
|
void FrameBuffer::updateEndAddress()
|
2015-03-11 10:59:42 +00:00
|
|
|
{
|
2017-09-25 15:24:30 +00:00
|
|
|
const u32 height = max(1U, m_height);
|
2017-03-02 16:24:42 +00:00
|
|
|
m_endAddress = min(RDRAMSize, m_startAddress + (((m_width * height) << m_size >> 1) - 1));
|
2015-03-11 10:59:42 +00:00
|
|
|
}
|
|
|
|
|
2015-04-29 13:58:06 +00:00
|
|
|
inline
|
|
|
|
u32 _cutHeight(u32 _address, u32 _height, u32 _stride)
|
|
|
|
{
|
2022-01-16 17:27:17 +00:00
|
|
|
#ifndef NATIVE
|
2015-11-15 09:26:03 +00:00
|
|
|
if (_address > RDRAMSize)
|
|
|
|
return 0;
|
2022-01-16 17:27:17 +00:00
|
|
|
|
2016-01-26 17:03:44 +00:00
|
|
|
if (_address + _stride * _height > (RDRAMSize + 1))
|
|
|
|
return (RDRAMSize + 1 - _address) / _stride;
|
2022-01-16 17:27:17 +00:00
|
|
|
#endif
|
2015-04-29 13:58:06 +00:00
|
|
|
return _height;
|
|
|
|
}
|
|
|
|
|
2016-09-27 14:43:28 +00:00
|
|
|
void FrameBuffer::setBufferClearParams(u32 _fillcolor, s32 _ulx, s32 _uly, s32 _lrx, s32 _lry)
|
|
|
|
{
|
|
|
|
m_cleared = true;
|
|
|
|
m_clearParams.fillcolor = _fillcolor;
|
|
|
|
m_clearParams.ulx = _ulx;
|
|
|
|
m_clearParams.lrx = _lrx;
|
|
|
|
m_clearParams.uly = _uly;
|
|
|
|
m_clearParams.lry = _lry;
|
|
|
|
}
|
|
|
|
|
2015-04-29 13:58:06 +00:00
|
|
|
void FrameBuffer::copyRdram()
|
|
|
|
{
|
|
|
|
const u32 stride = m_width << m_size >> 1;
|
|
|
|
const u32 height = _cutHeight(m_startAddress, m_height, stride);
|
2015-11-15 09:26:03 +00:00
|
|
|
if (height == 0)
|
|
|
|
return;
|
2021-05-15 13:43:05 +00:00
|
|
|
|
|
|
|
m_cleared = false;
|
|
|
|
|
2015-04-29 13:58:06 +00:00
|
|
|
const u32 dataSize = stride * height;
|
|
|
|
|
|
|
|
// Auxiliary frame buffer
|
2016-02-14 11:25:07 +00:00
|
|
|
if (isAuxiliary() && config.frameBufferEmulation.copyAuxToRDRAM == 0) {
|
2015-08-19 17:50:40 +00:00
|
|
|
// Write small amount of data to the start of the buffer.
|
|
|
|
// This is necessary for auxilary buffers: game can restore content of RDRAM when buffer is not needed anymore
|
|
|
|
// Thus content of RDRAM on moment of buffer creation will be the same as when buffer becomes obsolete.
|
|
|
|
// Validity check will see that the RDRAM is the same and thus the buffer is valid, which is false.
|
2016-05-07 17:20:10 +00:00
|
|
|
const u32 twoPercent = max(4U, dataSize / 200);
|
2015-08-19 17:50:40 +00:00
|
|
|
u32 start = m_startAddress >> 2;
|
2020-11-14 09:54:28 +00:00
|
|
|
u32 * pData = reinterpret_cast<u32*>(RDRAM);
|
2015-08-19 17:50:40 +00:00
|
|
|
for (u32 i = 0; i < twoPercent; ++i) {
|
|
|
|
if (i < 4)
|
|
|
|
pData[start++] = fingerprint[i];
|
|
|
|
else
|
|
|
|
pData[start++] = 0;
|
2015-04-29 13:58:06 +00:00
|
|
|
}
|
2015-08-19 17:50:40 +00:00
|
|
|
m_fingerprint = true;
|
|
|
|
return;
|
2015-04-29 13:58:06 +00:00
|
|
|
}
|
|
|
|
m_RdramCopy.resize(dataSize);
|
|
|
|
memcpy(m_RdramCopy.data(), RDRAM + m_startAddress, dataSize);
|
|
|
|
}
|
|
|
|
|
2017-09-26 08:23:07 +00:00
|
|
|
void FrameBuffer::setDirty()
|
|
|
|
{
|
|
|
|
m_cleared = false;
|
|
|
|
m_RdramCopy.clear();
|
|
|
|
}
|
|
|
|
|
2016-10-05 08:39:41 +00:00
|
|
|
bool FrameBuffer::isValid(bool _forceCheck) const
|
2015-04-29 13:58:06 +00:00
|
|
|
{
|
2016-10-05 08:39:41 +00:00
|
|
|
if (!_forceCheck) {
|
2017-01-15 07:57:25 +00:00
|
|
|
if (m_validityChecked == dwnd().getBuffersSwapCount())
|
2016-10-05 08:39:41 +00:00
|
|
|
return true; // Already checked
|
2017-01-15 07:57:25 +00:00
|
|
|
m_validityChecked = dwnd().getBuffersSwapCount();
|
2016-10-05 08:39:41 +00:00
|
|
|
}
|
2015-04-29 13:58:06 +00:00
|
|
|
|
2020-11-14 09:54:28 +00:00
|
|
|
const u32 * const pData = reinterpret_cast<const u32*>(RDRAM);
|
2015-04-29 13:58:06 +00:00
|
|
|
|
|
|
|
if (m_cleared) {
|
2016-09-27 14:43:28 +00:00
|
|
|
const u32 testColor = m_clearParams.fillcolor & 0xFFFEFFFE;
|
2017-03-11 06:07:24 +00:00
|
|
|
const u32 stride = m_width << m_size >> 1;
|
2020-11-14 09:54:28 +00:00
|
|
|
const s32 lry = static_cast<s32>(_cutHeight(m_startAddress, static_cast<u32>(m_clearParams.lry), stride));
|
2017-03-11 06:07:24 +00:00
|
|
|
if (lry == 0)
|
|
|
|
return false;
|
|
|
|
|
2016-09-27 14:43:28 +00:00
|
|
|
const u32 ci_width_in_dwords = m_width >> (3 - m_size);
|
2020-11-14 09:54:28 +00:00
|
|
|
const u32 start = (m_startAddress >> 2) + static_cast<u32>(m_clearParams.uly) * ci_width_in_dwords;
|
2016-09-27 14:43:28 +00:00
|
|
|
const u32 * dst = pData + start;
|
2015-04-29 13:58:06 +00:00
|
|
|
u32 wrongPixels = 0;
|
2017-11-15 07:42:04 +00:00
|
|
|
for (s32 y = m_clearParams.uly; y < lry; ++y) {
|
|
|
|
for (s32 x = m_clearParams.ulx; x < m_clearParams.lrx; ++x) {
|
2016-09-27 14:43:28 +00:00
|
|
|
if ((dst[x] & 0xFFFEFFFE) != testColor)
|
|
|
|
++wrongPixels;
|
|
|
|
}
|
|
|
|
dst += ci_width_in_dwords;
|
2015-04-29 13:58:06 +00:00
|
|
|
}
|
2015-12-14 18:27:53 +00:00
|
|
|
return wrongPixels < (m_endAddress - m_startAddress) / 400; // threshold level 1% of dwords
|
2015-08-17 15:45:08 +00:00
|
|
|
} else if (m_fingerprint) {
|
2015-07-21 11:32:52 +00:00
|
|
|
//check if our fingerprint is still there
|
|
|
|
u32 start = m_startAddress >> 2;
|
2015-07-21 15:26:35 +00:00
|
|
|
for (u32 i = 0; i < 4; ++i)
|
|
|
|
if ((pData[start++] & 0xFFFEFFFE) != (fingerprint[i] & 0xFFFEFFFE))
|
2015-07-21 11:32:52 +00:00
|
|
|
return false;
|
|
|
|
return true;
|
2015-08-17 15:45:08 +00:00
|
|
|
} else if (!m_RdramCopy.empty()) {
|
2018-05-06 08:54:12 +00:00
|
|
|
const u32 * const pCopy = reinterpret_cast<const u32* >(m_RdramCopy.data());
|
|
|
|
const u32 size = static_cast<u32>(m_RdramCopy.size());
|
2015-04-29 13:58:06 +00:00
|
|
|
const u32 size_dwords = size >> 2;
|
|
|
|
u32 start = m_startAddress >> 2;
|
|
|
|
u32 wrongPixels = 0;
|
|
|
|
for (u32 i = 0; i < size_dwords; ++i) {
|
|
|
|
if ((pData[start++] & 0xFFFEFFFE) != (pCopy[i] & 0xFFFEFFFE))
|
|
|
|
++wrongPixels;
|
|
|
|
}
|
2015-12-14 18:27:53 +00:00
|
|
|
return wrongPixels < size / 400; // threshold level 1% of dwords
|
2015-04-29 13:58:06 +00:00
|
|
|
}
|
|
|
|
return true; // No data to decide
|
|
|
|
}
|
2015-03-11 10:59:42 +00:00
|
|
|
|
2016-03-19 14:18:39 +00:00
|
|
|
void FrameBuffer::resolveMultisampledTexture(bool _bForce)
|
2015-02-09 12:27:39 +00:00
|
|
|
{
|
2018-04-06 17:17:04 +00:00
|
|
|
if (!Context::Multisampling)
|
2017-01-20 11:47:19 +00:00
|
|
|
return;
|
|
|
|
|
2016-03-19 14:18:39 +00:00
|
|
|
if (m_resolved && !_bForce)
|
2015-02-09 12:27:39 +00:00
|
|
|
return;
|
2017-01-20 11:47:19 +00:00
|
|
|
|
2020-11-22 19:37:55 +00:00
|
|
|
if (!m_pResolveTexture)
|
|
|
|
return;
|
|
|
|
|
2017-01-20 11:47:19 +00:00
|
|
|
Context::BlitFramebuffersParams blitParams;
|
|
|
|
blitParams.readBuffer = m_FBO;
|
|
|
|
blitParams.drawBuffer = m_resolveFBO;
|
|
|
|
blitParams.srcX0 = 0;
|
|
|
|
blitParams.srcY0 = 0;
|
2019-05-08 14:04:24 +00:00
|
|
|
blitParams.srcX1 = m_pTexture->width;
|
|
|
|
blitParams.srcY1 = m_pTexture->height;
|
2017-01-20 11:47:19 +00:00
|
|
|
blitParams.dstX0 = 0;
|
|
|
|
blitParams.dstY0 = 0;
|
2019-05-08 14:04:24 +00:00
|
|
|
blitParams.dstX1 = m_pResolveTexture->width;
|
|
|
|
blitParams.dstY1 = m_pResolveTexture->height;
|
2017-01-20 11:47:19 +00:00
|
|
|
blitParams.mask = blitMask::COLOR_BUFFER;
|
|
|
|
blitParams.filter = textureParameters::FILTER_NEAREST;
|
|
|
|
|
|
|
|
gfxContext.blitFramebuffers(blitParams);
|
|
|
|
|
2018-04-03 15:08:06 +00:00
|
|
|
gfxContext.bindFramebuffer(bufferTarget::READ_FRAMEBUFFER, ObjectHandle::defaultFramebuffer);
|
2017-01-20 11:47:19 +00:00
|
|
|
|
2016-05-08 07:35:17 +00:00
|
|
|
frameBufferList().setCurrentDrawBuffer();
|
2015-02-09 12:27:39 +00:00
|
|
|
m_resolved = true;
|
|
|
|
}
|
|
|
|
|
2019-06-27 09:21:51 +00:00
|
|
|
void FrameBuffer::copyDepthTexture()
|
|
|
|
{
|
|
|
|
if (config.frameBufferEmulation.copyDepthToMainDepthBuffer != 0)
|
|
|
|
DepthBuffer::copyDepthBufferTexture(this, m_pDepthTexture, m_depthFBO);
|
|
|
|
}
|
|
|
|
|
2016-05-08 09:58:19 +00:00
|
|
|
bool FrameBuffer::_initSubTexture(u32 _t)
|
2015-02-09 12:27:39 +00:00
|
|
|
{
|
2017-01-20 11:47:19 +00:00
|
|
|
if (!m_SubFBO.isNotNull())
|
|
|
|
m_SubFBO = gfxContext.createFramebuffer();
|
2016-05-08 09:58:19 +00:00
|
|
|
|
|
|
|
gDPTile * pTile = gSP.textureTile[_t];
|
|
|
|
if (pTile->lrs < pTile->uls || pTile->lrt < pTile->ult)
|
|
|
|
return false;
|
|
|
|
const u32 width = pTile->lrs - pTile->uls + 1;
|
|
|
|
const u32 height = pTile->lrt - pTile->ult + 1;
|
|
|
|
|
2016-06-10 06:06:06 +00:00
|
|
|
if (m_pSubTexture != nullptr) {
|
2016-05-08 09:58:19 +00:00
|
|
|
if (m_pSubTexture->size == m_pTexture->size &&
|
|
|
|
m_pSubTexture->clampWidth == width &&
|
|
|
|
m_pSubTexture->clampHeight == height)
|
|
|
|
return true;
|
|
|
|
textureCache().removeFrameBufferTexture(m_pSubTexture);
|
|
|
|
}
|
|
|
|
|
2019-11-29 22:09:09 +00:00
|
|
|
m_pSubTexture = textureCache().addFrameBufferTexture(textureTarget::TEXTURE_2D);
|
2020-11-14 09:54:28 +00:00
|
|
|
_initTexture(static_cast<u16>(width), static_cast<u16>(height), m_pTexture->format, m_pTexture->size, m_pSubTexture);
|
2016-05-08 09:58:19 +00:00
|
|
|
|
|
|
|
m_pSubTexture->clampS = pTile->clamps;
|
|
|
|
m_pSubTexture->clampT = pTile->clampt;
|
|
|
|
m_pSubTexture->offsetS = 0.0f;
|
2017-02-27 09:34:10 +00:00
|
|
|
m_pSubTexture->offsetT = 0.0f;
|
2020-04-18 16:20:17 +00:00
|
|
|
m_pSubTexture->hdRatioS = m_pTexture->hdRatioS;
|
|
|
|
m_pSubTexture->hdRatioT = m_pTexture->hdRatioT;
|
2016-05-08 09:58:19 +00:00
|
|
|
|
2017-01-01 17:09:19 +00:00
|
|
|
|
2017-01-02 14:00:49 +00:00
|
|
|
_setAndAttachTexture(m_SubFBO, m_pSubTexture, _t, false);
|
2017-01-01 17:09:19 +00:00
|
|
|
|
2016-05-08 09:58:19 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
CachedTexture * FrameBuffer::_getSubTexture(u32 _t)
|
|
|
|
{
|
2018-04-06 17:17:04 +00:00
|
|
|
if (!Context::BlitFramebuffer)
|
2017-01-20 11:47:19 +00:00
|
|
|
return m_pTexture;
|
|
|
|
|
2016-05-08 09:58:19 +00:00
|
|
|
if (!_initSubTexture(_t))
|
2015-02-09 12:27:39 +00:00
|
|
|
return m_pTexture;
|
2017-01-20 11:47:19 +00:00
|
|
|
|
2020-11-14 09:54:28 +00:00
|
|
|
s32 x0 = static_cast<s32>(m_pTexture->offsetS * m_scale);
|
|
|
|
s32 y0 = static_cast<s32>(m_pTexture->offsetT * m_scale);
|
2019-05-08 14:04:24 +00:00
|
|
|
s32 copyWidth = m_pSubTexture->width;
|
|
|
|
if (x0 + copyWidth > m_pTexture->width)
|
|
|
|
copyWidth = m_pTexture->width - x0;
|
|
|
|
s32 copyHeight = m_pSubTexture->height;
|
|
|
|
if (y0 + copyHeight > m_pTexture->height)
|
|
|
|
copyHeight = m_pTexture->height - y0;
|
2016-05-08 09:58:19 +00:00
|
|
|
|
2017-01-20 11:47:19 +00:00
|
|
|
ObjectHandle readFBO = m_FBO;
|
2018-04-06 17:17:04 +00:00
|
|
|
if (Context::WeakBlitFramebuffer &&
|
2017-01-20 11:47:19 +00:00
|
|
|
m_pTexture->frameBufferTexture == CachedTexture::fbMultiSample) {
|
2016-11-03 05:53:26 +00:00
|
|
|
resolveMultisampledTexture(true);
|
2017-01-20 11:47:19 +00:00
|
|
|
readFBO = m_resolveFBO;
|
|
|
|
}
|
|
|
|
|
|
|
|
Context::BlitFramebuffersParams blitParams;
|
|
|
|
blitParams.readBuffer = readFBO;
|
|
|
|
blitParams.drawBuffer = m_SubFBO;
|
|
|
|
blitParams.srcX0 = x0;
|
|
|
|
blitParams.srcY0 = y0;
|
|
|
|
blitParams.srcX1 = x0 + copyWidth;
|
|
|
|
blitParams.srcY1 = y0 + copyHeight;
|
|
|
|
blitParams.dstX0 = 0;
|
|
|
|
blitParams.dstY0 = 0;
|
|
|
|
blitParams.dstX1 = copyWidth;
|
|
|
|
blitParams.dstY1 = copyHeight;
|
|
|
|
blitParams.mask = blitMask::COLOR_BUFFER;
|
|
|
|
blitParams.filter = textureParameters::FILTER_NEAREST;
|
|
|
|
|
|
|
|
gfxContext.blitFramebuffers(blitParams);
|
|
|
|
|
2018-04-03 15:08:06 +00:00
|
|
|
gfxContext.bindFramebuffer(bufferTarget::READ_FRAMEBUFFER, ObjectHandle::defaultFramebuffer);
|
2017-01-20 11:47:19 +00:00
|
|
|
|
2016-05-08 09:58:19 +00:00
|
|
|
frameBufferList().setCurrentDrawBuffer();
|
|
|
|
|
|
|
|
return m_pSubTexture;
|
|
|
|
}
|
|
|
|
|
2018-04-16 19:13:18 +00:00
|
|
|
void FrameBuffer::_initCopyTexture()
|
|
|
|
{
|
|
|
|
m_copyFBO = gfxContext.createFramebuffer();
|
2019-11-29 22:09:09 +00:00
|
|
|
m_pFrameBufferCopyTexture = textureCache().addFrameBufferTexture(config.video.multisampling != 0 ?
|
|
|
|
textureTarget::TEXTURE_2D_MULTISAMPLE : textureTarget::TEXTURE_2D);
|
2020-11-14 09:54:28 +00:00
|
|
|
_initTexture(static_cast<u16>(m_width), VI_GetMaxBufferHeight(static_cast<u16>(m_width)),
|
|
|
|
m_pTexture->format, m_pTexture->size, m_pFrameBufferCopyTexture);
|
2018-04-16 19:13:18 +00:00
|
|
|
_setAndAttachTexture(m_copyFBO, m_pFrameBufferCopyTexture, 0, config.video.multisampling != 0);
|
|
|
|
if (config.video.multisampling != 0)
|
|
|
|
m_pFrameBufferCopyTexture->frameBufferTexture = CachedTexture::fbMultiSample;
|
|
|
|
}
|
|
|
|
|
|
|
|
CachedTexture * FrameBuffer::_copyFrameBufferTexture()
|
|
|
|
{
|
|
|
|
if (m_copied)
|
|
|
|
return m_pFrameBufferCopyTexture;
|
|
|
|
|
|
|
|
if (m_pFrameBufferCopyTexture == nullptr)
|
|
|
|
_initCopyTexture();
|
|
|
|
|
|
|
|
Context::BlitFramebuffersParams blitParams;
|
|
|
|
blitParams.readBuffer = m_FBO;
|
|
|
|
blitParams.drawBuffer = m_copyFBO;
|
|
|
|
blitParams.srcX0 = 0;
|
|
|
|
blitParams.srcY0 = 0;
|
2019-05-08 14:04:24 +00:00
|
|
|
blitParams.srcX1 = m_pTexture->width;
|
|
|
|
blitParams.srcY1 = m_pTexture->height;
|
2018-04-16 19:13:18 +00:00
|
|
|
blitParams.dstX0 = 0;
|
|
|
|
blitParams.dstY0 = 0;
|
2019-05-08 14:04:24 +00:00
|
|
|
blitParams.dstX1 = m_pTexture->width;
|
|
|
|
blitParams.dstY1 = m_pTexture->height;
|
2018-04-16 19:13:18 +00:00
|
|
|
blitParams.mask = blitMask::COLOR_BUFFER;
|
|
|
|
blitParams.filter = textureParameters::FILTER_NEAREST;
|
|
|
|
|
|
|
|
gfxContext.blitFramebuffers(blitParams);
|
|
|
|
|
2018-04-03 15:08:06 +00:00
|
|
|
gfxContext.bindFramebuffer(bufferTarget::READ_FRAMEBUFFER, ObjectHandle::defaultFramebuffer);
|
2018-04-16 19:13:18 +00:00
|
|
|
frameBufferList().setCurrentDrawBuffer();
|
|
|
|
|
|
|
|
m_copied = true;
|
|
|
|
return m_pFrameBufferCopyTexture;
|
|
|
|
}
|
|
|
|
|
2016-05-08 09:58:19 +00:00
|
|
|
CachedTexture * FrameBuffer::getTexture(u32 _t)
|
|
|
|
{
|
2016-09-06 12:37:11 +00:00
|
|
|
const bool getDepthTexture = m_isDepthBuffer &&
|
|
|
|
gDP.colorImage.address == gDP.depthImageAddress &&
|
2016-09-07 14:11:31 +00:00
|
|
|
m_pDepthBuffer != nullptr &&
|
2016-09-11 08:27:59 +00:00
|
|
|
(config.generalEmulation.hacks & hack_ZeldaMM) == 0;
|
2016-10-25 05:27:49 +00:00
|
|
|
CachedTexture *pTexture = getDepthTexture ? m_pDepthBuffer->m_pDepthBufferTexture : m_pTexture;
|
2016-05-08 09:58:19 +00:00
|
|
|
|
2018-04-16 19:13:18 +00:00
|
|
|
if (this == frameBufferList().getCurrent()) {
|
|
|
|
if (Context::TextureBarrier)
|
|
|
|
gfxContext.textureBarrier();
|
|
|
|
else if (Context::BlitFramebuffer)
|
|
|
|
pTexture = getDepthTexture ? m_pDepthBuffer->copyDepthBufferTexture(this) : _copyFrameBufferTexture();
|
|
|
|
}
|
|
|
|
|
2016-10-25 05:27:49 +00:00
|
|
|
const u32 shift = (gSP.textureTile[_t]->imageAddress - m_startAddress) >> (m_size - 1);
|
|
|
|
const u32 factor = m_width;
|
|
|
|
if (m_loadType == LOADTYPE_TILE) {
|
2020-11-14 09:54:28 +00:00
|
|
|
pTexture->offsetS = static_cast<f32>(m_loadTileOrigin.uls + (shift % factor));
|
|
|
|
pTexture->offsetT = static_cast<f32>(m_loadTileOrigin.ult + shift / factor);
|
2016-10-25 05:27:49 +00:00
|
|
|
} else {
|
2020-11-14 09:54:28 +00:00
|
|
|
pTexture->offsetS = static_cast<f32>(shift % factor);
|
|
|
|
pTexture->offsetT = static_cast<f32>(shift / factor);
|
2016-09-29 09:54:29 +00:00
|
|
|
}
|
2021-10-02 13:14:52 +00:00
|
|
|
pTexture->hdRatioS = m_pTexture->hdRatioS;
|
|
|
|
pTexture->hdRatioT = m_pTexture->hdRatioT;
|
2016-09-29 09:54:29 +00:00
|
|
|
|
2016-10-25 05:27:49 +00:00
|
|
|
if (!getDepthTexture && (gSP.textureTile[_t]->clamps == 0 || gSP.textureTile[_t]->clampt == 0))
|
|
|
|
pTexture = _getSubTexture(_t);
|
|
|
|
|
2020-11-14 09:54:28 +00:00
|
|
|
pTexture->scaleS = m_scale / static_cast<f32>(pTexture->width);
|
|
|
|
pTexture->scaleT = m_scale / static_cast<f32>(pTexture->height);
|
2016-05-08 09:58:19 +00:00
|
|
|
|
2021-11-06 09:57:36 +00:00
|
|
|
pTexture->shiftScaleS = calcShiftScaleS(*gSP.textureTile[_t]);
|
|
|
|
pTexture->shiftScaleT = calcShiftScaleT(*gSP.textureTile[_t]);
|
2016-05-08 09:58:19 +00:00
|
|
|
|
|
|
|
return pTexture;
|
|
|
|
}
|
2015-02-09 12:27:39 +00:00
|
|
|
|
2020-11-14 09:54:28 +00:00
|
|
|
CachedTexture * FrameBuffer::getTextureBG()
|
2016-05-08 09:58:19 +00:00
|
|
|
{
|
2018-04-16 19:13:18 +00:00
|
|
|
CachedTexture *pTexture = m_pTexture;
|
|
|
|
|
|
|
|
if (this == frameBufferList().getCurrent()) {
|
|
|
|
if (Context::TextureBarrier)
|
|
|
|
gfxContext.textureBarrier();
|
|
|
|
else if (Context::BlitFramebuffer)
|
|
|
|
pTexture = _copyFrameBufferTexture();
|
|
|
|
}
|
2016-05-08 09:58:19 +00:00
|
|
|
|
2020-11-14 09:54:28 +00:00
|
|
|
pTexture->scaleS = m_scale / static_cast<f32>(pTexture->width);
|
|
|
|
pTexture->scaleT = m_scale / static_cast<f32>(pTexture->height);
|
2016-05-08 09:58:19 +00:00
|
|
|
|
2018-04-16 19:13:18 +00:00
|
|
|
pTexture->shiftScaleS = 1.0f;
|
|
|
|
pTexture->shiftScaleT = 1.0f;
|
|
|
|
|
|
|
|
pTexture->offsetS = gSP.bgImage.imageX;
|
|
|
|
pTexture->offsetT = gSP.bgImage.imageY;
|
|
|
|
return pTexture;
|
2013-04-05 06:13:26 +00:00
|
|
|
}
|
|
|
|
|
2021-04-18 00:45:47 +00:00
|
|
|
CachedTexture * FrameBuffer::getColorFbTexture()
|
|
|
|
{
|
|
|
|
if(m_pColorBufferTexture == nullptr ||
|
|
|
|
m_pColorBufferTexture->width != m_width ||
|
|
|
|
m_pColorBufferTexture->height != VI_GetMaxBufferHeight(m_width))
|
|
|
|
{
|
|
|
|
_destroyColorFBTexure();
|
|
|
|
_initColorFBTexture(m_width);
|
|
|
|
}
|
|
|
|
|
|
|
|
return m_pColorBufferTexture;
|
|
|
|
}
|
|
|
|
|
|
|
|
graphics::ObjectHandle FrameBuffer::getColorFbFbo()
|
|
|
|
{
|
|
|
|
return m_ColorBufferFBO;
|
|
|
|
}
|
|
|
|
|
|
|
|
const u8 * FrameBuffer::readPixels(s32 _x0, s32 _y0, u32 _width, u32 _height, u32 _size, bool _sync)
|
|
|
|
{
|
|
|
|
return m_bufferReader->readPixels(_x0, _y0, _width, _height, _size, _sync);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FrameBuffer::cleanUp()
|
|
|
|
{
|
|
|
|
m_bufferReader->cleanUp();
|
|
|
|
}
|
|
|
|
|
|
|
|
void FrameBuffer::_initColorFBTexture(int _width)
|
|
|
|
{
|
|
|
|
m_ColorBufferFBO = gfxContext.createFramebuffer();
|
|
|
|
|
|
|
|
const FramebufferTextureFormats & fbTexFormat = gfxContext.getFramebufferTextureFormats();
|
|
|
|
|
|
|
|
m_pColorBufferTexture = textureCache().addFrameBufferTexture(Context::EglImage ? textureTarget::TEXTURE_EXTERNAL : textureTarget::TEXTURE_2D);
|
|
|
|
m_pColorBufferTexture->format = G_IM_FMT_RGBA;
|
|
|
|
m_pColorBufferTexture->size = 2;
|
|
|
|
m_pColorBufferTexture->clampS = 1;
|
|
|
|
m_pColorBufferTexture->clampT = 1;
|
|
|
|
m_pColorBufferTexture->frameBufferTexture = CachedTexture::fbOneSample;
|
|
|
|
m_pColorBufferTexture->maskS = 0;
|
|
|
|
m_pColorBufferTexture->maskT = 0;
|
|
|
|
m_pColorBufferTexture->mirrorS = 0;
|
|
|
|
m_pColorBufferTexture->mirrorT = 0;
|
|
|
|
m_pColorBufferTexture->width = _width;
|
|
|
|
m_pColorBufferTexture->height = VI_GetMaxBufferHeight(_width);
|
|
|
|
m_pColorBufferTexture->textureBytes = m_pColorBufferTexture->width * m_pColorBufferTexture->height * fbTexFormat.colorFormatBytes;
|
|
|
|
|
|
|
|
m_bufferReader.reset(gfxContext.createColorBufferReader(m_pColorBufferTexture));
|
|
|
|
|
|
|
|
// Skip this since texture is initialized in the EGL color buffer reader
|
|
|
|
if (!Context::EglImage)
|
|
|
|
{
|
|
|
|
Context::InitTextureParams params;
|
|
|
|
params.handle = m_pColorBufferTexture->name;
|
|
|
|
params.target = textureTarget::TEXTURE_2D;
|
|
|
|
params.width = m_pColorBufferTexture->width;
|
|
|
|
params.height = m_pColorBufferTexture->height;
|
|
|
|
params.internalFormat = fbTexFormat.colorInternalFormat;
|
|
|
|
params.format = fbTexFormat.colorFormat;
|
|
|
|
params.dataType = fbTexFormat.colorType;
|
|
|
|
gfxContext.init2DTexture(params);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
Context::TexParameters params;
|
|
|
|
params.handle = m_pColorBufferTexture->name;
|
|
|
|
params.target = Context::EglImage ? textureTarget::TEXTURE_EXTERNAL : textureTarget::TEXTURE_2D;
|
|
|
|
params.textureUnitIndex = textureIndices::Tex[0];
|
|
|
|
params.minFilter = textureParameters::FILTER_LINEAR;
|
|
|
|
params.magFilter = textureParameters::FILTER_LINEAR;
|
|
|
|
gfxContext.setTextureParameters(params);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
Context::FrameBufferRenderTarget bufTarget;
|
|
|
|
bufTarget.bufferHandle = ObjectHandle(m_ColorBufferFBO);
|
|
|
|
bufTarget.bufferTarget = bufferTarget::DRAW_FRAMEBUFFER;
|
|
|
|
bufTarget.attachment = bufferAttachment::COLOR_ATTACHMENT0;
|
|
|
|
bufTarget.textureTarget = Context::EglImageFramebuffer ? textureTarget::TEXTURE_EXTERNAL : textureTarget::TEXTURE_2D;
|
|
|
|
bufTarget.textureHandle = m_pColorBufferTexture->name;
|
|
|
|
gfxContext.addFrameBufferRenderTarget(bufTarget);
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if everything is OK
|
|
|
|
assert(!gfxContext.isFramebufferError());
|
|
|
|
|
|
|
|
gfxContext.bindFramebuffer(graphics::bufferTarget::DRAW_FRAMEBUFFER, graphics::ObjectHandle::defaultFramebuffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FrameBuffer::_destroyColorFBTexure()
|
|
|
|
{
|
|
|
|
m_bufferReader.reset();
|
|
|
|
|
|
|
|
if (m_pColorBufferTexture != nullptr) {
|
|
|
|
textureCache().removeFrameBufferTexture(m_pColorBufferTexture);
|
|
|
|
m_pColorBufferTexture = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_ColorBufferFBO.isNotNull()) {
|
|
|
|
gfxContext.deleteFramebuffer(m_ColorBufferFBO);
|
|
|
|
m_ColorBufferFBO.reset();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-31 12:44:41 +00:00
|
|
|
FrameBufferList & FrameBufferList::get()
|
|
|
|
{
|
|
|
|
static FrameBufferList frameBufferList;
|
|
|
|
return frameBufferList;
|
|
|
|
}
|
|
|
|
|
2014-09-08 11:01:22 +00:00
|
|
|
void FrameBufferList::init()
|
2013-04-05 06:13:26 +00:00
|
|
|
{
|
2016-06-10 06:06:06 +00:00
|
|
|
m_pCurrent = nullptr;
|
|
|
|
m_pCopy = nullptr;
|
2018-04-03 15:08:06 +00:00
|
|
|
gfxContext.bindFramebuffer(bufferTarget::DRAW_FRAMEBUFFER, ObjectHandle::defaultFramebuffer);
|
2016-02-14 11:25:07 +00:00
|
|
|
m_prevColorImageHeight = 0;
|
2018-05-05 07:45:18 +00:00
|
|
|
m_overscan.init();
|
2019-01-09 14:54:31 +00:00
|
|
|
m_rdpUpdate.init();
|
2014-09-08 11:01:22 +00:00
|
|
|
}
|
2013-04-05 06:13:26 +00:00
|
|
|
|
2014-09-08 11:01:22 +00:00
|
|
|
void FrameBufferList::destroy() {
|
2018-04-03 15:08:06 +00:00
|
|
|
gfxContext.bindFramebuffer(bufferTarget::FRAMEBUFFER, ObjectHandle::defaultFramebuffer);
|
2014-09-08 11:01:22 +00:00
|
|
|
m_list.clear();
|
2016-06-10 06:06:06 +00:00
|
|
|
m_pCurrent = nullptr;
|
|
|
|
m_pCopy = nullptr;
|
2018-05-05 07:45:18 +00:00
|
|
|
m_overscan.destroy();
|
2013-04-05 06:13:26 +00:00
|
|
|
}
|
|
|
|
|
2017-02-26 15:07:47 +00:00
|
|
|
void FrameBufferList::setBufferChanged(f32 _maxY)
|
2015-03-01 11:52:59 +00:00
|
|
|
{
|
|
|
|
gDP.colorImage.changed = TRUE;
|
2020-11-14 09:54:28 +00:00
|
|
|
gDP.colorImage.height = max(gDP.colorImage.height, static_cast<u32>(_maxY));
|
|
|
|
gDP.colorImage.height = min(gDP.colorImage.height, static_cast<u32>(gDP.scissor.lry));
|
2016-06-10 06:06:06 +00:00
|
|
|
if (m_pCurrent != nullptr) {
|
2021-11-20 10:22:24 +00:00
|
|
|
if (m_pCurrent->m_isMainBuffer)
|
|
|
|
m_pCurrent->m_height = max(m_pCurrent->m_height, min(gDP.colorImage.height, VI.height));
|
|
|
|
else
|
|
|
|
m_pCurrent->m_height = max(m_pCurrent->m_height, gDP.colorImage.height);
|
2016-09-19 08:32:24 +00:00
|
|
|
m_pCurrent->m_cfb = false;
|
2015-03-01 11:52:59 +00:00
|
|
|
m_pCurrent->m_changed = true;
|
2015-04-29 13:58:06 +00:00
|
|
|
m_pCurrent->m_copiedToRdram = false;
|
2015-03-09 11:17:20 +00:00
|
|
|
}
|
2015-03-01 11:52:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void FrameBufferList::clearBuffersChanged()
|
|
|
|
{
|
|
|
|
gDP.colorImage.changed = FALSE;
|
2022-02-08 13:01:30 +00:00
|
|
|
#ifndef NATIVE
|
|
|
|
FrameBuffer * pBuffer = frameBufferList().findBuffer(*REG.VI_ORIGIN & 0xffffff);
|
|
|
|
#else
|
2022-02-08 02:44:17 +00:00
|
|
|
FrameBuffer* pBuffer = frameBufferList().findBuffer(*REG.VI_ORIGIN);
|
2022-02-08 13:01:30 +00:00
|
|
|
#endif
|
2016-06-10 06:06:06 +00:00
|
|
|
if (pBuffer != nullptr)
|
2015-03-30 12:06:21 +00:00
|
|
|
pBuffer->m_changed = false;
|
2015-03-01 11:52:59 +00:00
|
|
|
}
|
|
|
|
|
2016-05-08 07:35:17 +00:00
|
|
|
void FrameBufferList::setCurrentDrawBuffer() const
|
|
|
|
{
|
|
|
|
if (m_pCurrent != nullptr)
|
2017-01-20 11:47:19 +00:00
|
|
|
gfxContext.bindFramebuffer(bufferTarget::DRAW_FRAMEBUFFER, m_pCurrent->m_FBO);
|
2018-03-21 14:20:57 +00:00
|
|
|
else if (!m_list.empty())
|
|
|
|
gfxContext.bindFramebuffer(bufferTarget::DRAW_FRAMEBUFFER, m_list.back().m_FBO);
|
2016-05-08 07:35:17 +00:00
|
|
|
}
|
|
|
|
|
2014-10-22 07:49:55 +00:00
|
|
|
FrameBuffer * FrameBufferList::findBuffer(u32 _startAddress)
|
2013-04-05 06:13:26 +00:00
|
|
|
{
|
2017-01-20 11:47:19 +00:00
|
|
|
for (auto iter = m_list.begin(); iter != m_list.end(); ++iter) {
|
|
|
|
if (iter->m_startAddress <= _startAddress && iter->m_endAddress >= _startAddress) // [ { ]
|
|
|
|
return &(*iter);
|
|
|
|
}
|
2016-06-10 06:06:06 +00:00
|
|
|
return nullptr;
|
2014-10-22 07:49:55 +00:00
|
|
|
}
|
|
|
|
|
2017-09-29 15:24:30 +00:00
|
|
|
FrameBuffer * FrameBufferList::getBuffer(u32 _startAddress)
|
|
|
|
{
|
|
|
|
for (auto iter = m_list.begin(); iter != m_list.end(); ++iter) {
|
|
|
|
if (iter->m_startAddress == _startAddress)
|
|
|
|
return &(*iter);
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2017-09-25 15:24:30 +00:00
|
|
|
inline
|
|
|
|
bool isOverlapping(const FrameBuffer * _buf1, const FrameBuffer * _buf2)
|
|
|
|
{
|
|
|
|
if (_buf1->m_endAddress < _buf2->m_endAddress && _buf1->m_width == _buf2->m_width && _buf1->m_size == _buf2->m_size) {
|
|
|
|
const u32 diff = _buf1->m_endAddress - _buf2->m_startAddress + 1;
|
|
|
|
const u32 stride = _buf1->m_width << _buf1->m_size >> 1;
|
|
|
|
if ((diff % stride == 0) && (diff / stride < 5))
|
|
|
|
return true;
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-03-05 14:35:20 +00:00
|
|
|
void FrameBufferList::removeIntersections()
|
2014-10-22 07:49:55 +00:00
|
|
|
{
|
2017-03-05 14:35:20 +00:00
|
|
|
assert(!m_list.empty());
|
2014-10-22 07:49:55 +00:00
|
|
|
|
|
|
|
FrameBuffers::iterator iter = m_list.end();
|
|
|
|
do {
|
|
|
|
--iter;
|
2017-03-05 14:35:20 +00:00
|
|
|
if (&(*iter) == m_pCurrent)
|
|
|
|
continue;
|
2017-09-25 15:24:30 +00:00
|
|
|
if (iter->m_startAddress <= m_pCurrent->m_startAddress && iter->m_endAddress >= m_pCurrent->m_startAddress) { // [ { ]
|
|
|
|
if (isOverlapping(&(*iter), m_pCurrent)) {
|
|
|
|
iter->m_endAddress = m_pCurrent->m_startAddress - 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
iter = m_list.erase(iter);
|
|
|
|
} else if (m_pCurrent->m_startAddress <= iter->m_startAddress && m_pCurrent->m_endAddress >= iter->m_startAddress) { // { [ }
|
|
|
|
if (isOverlapping(m_pCurrent, &(*iter))) {
|
|
|
|
m_pCurrent->m_endAddress = iter->m_startAddress - 1;
|
|
|
|
continue;
|
|
|
|
}
|
2017-03-05 14:35:20 +00:00
|
|
|
iter = m_list.erase(iter);
|
2014-10-22 07:49:55 +00:00
|
|
|
}
|
|
|
|
} while (iter != m_list.begin());
|
2013-04-05 06:13:26 +00:00
|
|
|
}
|
|
|
|
|
2014-09-08 11:01:22 +00:00
|
|
|
FrameBuffer * FrameBufferList::findTmpBuffer(u32 _address)
|
2013-04-05 06:13:26 +00:00
|
|
|
{
|
2017-01-20 11:47:19 +00:00
|
|
|
for (auto iter = m_list.begin(); iter != m_list.end(); ++iter)
|
2014-09-08 11:01:22 +00:00
|
|
|
if (iter->m_startAddress > _address || iter->m_endAddress < _address)
|
|
|
|
return &(*iter);
|
2016-06-10 06:06:06 +00:00
|
|
|
return nullptr;
|
2013-04-05 06:13:26 +00:00
|
|
|
}
|
|
|
|
|
2018-10-25 10:29:50 +00:00
|
|
|
void FrameBufferList::updateCurrentBufferEndAddress()
|
|
|
|
{
|
|
|
|
if (m_pCurrent == nullptr)
|
|
|
|
return;
|
|
|
|
m_pCurrent->updateEndAddress();
|
|
|
|
removeIntersections();
|
|
|
|
}
|
2017-02-25 06:38:33 +00:00
|
|
|
|
|
|
|
void FrameBufferList::_createScreenSizeBuffer()
|
|
|
|
{
|
|
|
|
if (VI.height == 0)
|
|
|
|
return;
|
|
|
|
m_list.emplace_front();
|
|
|
|
FrameBuffer & buffer = m_list.front();
|
2020-11-14 09:54:28 +00:00
|
|
|
buffer.init(VI.width * 2, G_IM_FMT_RGBA, G_IM_SIZ_16b, static_cast<u16>(VI.width), false);
|
2017-02-25 06:38:33 +00:00
|
|
|
}
|
|
|
|
|
2017-03-02 15:09:08 +00:00
|
|
|
void FrameBufferList::saveBuffer(u32 _address, u16 _format, u16 _size, u16 _width, bool _cfb)
|
2013-04-05 06:13:26 +00:00
|
|
|
{
|
2017-03-10 10:16:10 +00:00
|
|
|
if (_width > 640)
|
|
|
|
return;
|
|
|
|
|
2017-03-31 06:14:15 +00:00
|
|
|
if (_width == 512 && (config.generalEmulation.hacks & hack_RE2) != 0)
|
2020-11-14 09:54:28 +00:00
|
|
|
_width = static_cast<u16>(*REG.VI_WIDTH);
|
2017-03-31 06:14:15 +00:00
|
|
|
|
2017-02-25 06:38:33 +00:00
|
|
|
if (config.frameBufferEmulation.enable == 0) {
|
|
|
|
if (m_list.empty())
|
|
|
|
_createScreenSizeBuffer();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-01-20 11:47:19 +00:00
|
|
|
if (m_pCurrent != nullptr &&
|
|
|
|
config.frameBufferEmulation.copyAuxToRDRAM != 0 &&
|
|
|
|
(config.generalEmulation.hacks & hack_Snap) == 0) {
|
2016-02-14 11:25:07 +00:00
|
|
|
if (m_pCurrent->isAuxiliary()) {
|
2015-10-31 03:11:33 +00:00
|
|
|
FrameBuffer_CopyToRDRAM(m_pCurrent->m_startAddress, true);
|
2015-10-31 02:39:14 +00:00
|
|
|
removeBuffer(m_pCurrent->m_startAddress);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-15 07:57:25 +00:00
|
|
|
DisplayWindow & wnd = dwnd();
|
2016-09-11 11:08:03 +00:00
|
|
|
bool bPrevIsDepth = false;
|
|
|
|
|
2016-06-10 06:06:06 +00:00
|
|
|
if (m_pCurrent != nullptr) {
|
2016-09-11 11:08:03 +00:00
|
|
|
bPrevIsDepth = m_pCurrent->m_isDepthBuffer;
|
2016-10-29 16:46:39 +00:00
|
|
|
m_pCurrent->m_readable = true;
|
2017-03-02 15:09:08 +00:00
|
|
|
m_pCurrent->updateEndAddress();
|
|
|
|
|
2018-09-15 15:46:15 +00:00
|
|
|
if (!m_pCurrent->m_isDepthBuffer &&
|
2017-03-02 15:09:08 +00:00
|
|
|
!m_pCurrent->m_copiedToRdram &&
|
|
|
|
!m_pCurrent->m_cfb &&
|
2018-09-14 05:21:47 +00:00
|
|
|
!m_pCurrent->m_cleared &&
|
|
|
|
m_pCurrent->m_RdramCopy.empty() &&
|
|
|
|
m_pCurrent->m_height > 1) {
|
2016-02-14 11:25:07 +00:00
|
|
|
m_pCurrent->copyRdram();
|
2015-03-19 14:42:09 +00:00
|
|
|
}
|
2016-02-14 11:25:07 +00:00
|
|
|
|
2017-03-05 14:35:20 +00:00
|
|
|
removeIntersections();
|
2013-08-10 11:10:44 +00:00
|
|
|
}
|
|
|
|
|
2018-09-25 09:37:57 +00:00
|
|
|
const float scaleX = config.frameBufferEmulation.nativeResFactor == 0 ?
|
|
|
|
wnd.getScaleX() :
|
|
|
|
static_cast<float>(config.frameBufferEmulation.nativeResFactor);
|
|
|
|
|
2016-06-10 06:06:06 +00:00
|
|
|
if (m_pCurrent == nullptr || m_pCurrent->m_startAddress != _address || m_pCurrent->m_width != _width)
|
2015-03-11 14:59:06 +00:00
|
|
|
m_pCurrent = findBuffer(_address);
|
2018-09-25 09:37:57 +00:00
|
|
|
|
|
|
|
auto isSubBuffer = [_address, _width, _size, &wnd](const FrameBuffer * _pBuffer) -> bool
|
|
|
|
{
|
|
|
|
if (_pBuffer->m_swapCount == wnd.getBuffersSwapCount() &&
|
|
|
|
!_pBuffer->m_cfb &&
|
|
|
|
_pBuffer->m_width == _width &&
|
|
|
|
_pBuffer->m_size == _size)
|
2013-04-05 06:13:26 +00:00
|
|
|
{
|
2018-09-25 09:37:57 +00:00
|
|
|
const u32 stride = _width << _size >> 1;
|
|
|
|
const u32 diffFromStart = _address - _pBuffer->m_startAddress;
|
|
|
|
if (diffFromStart % stride != 0)
|
|
|
|
return true;
|
|
|
|
const u32 diffFromEnd = _pBuffer->m_endAddress - _address + 1;
|
|
|
|
if ((diffFromEnd / stride > 5))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
auto isOverlappingBuffer = [_address, _width, _size](const FrameBuffer * _pBuffer) -> bool
|
|
|
|
{
|
|
|
|
if (_pBuffer->m_width == _width && _pBuffer->m_size == _size) {
|
|
|
|
const u32 stride = _width << _size >> 1;
|
|
|
|
const u32 diffEnd = _pBuffer->m_endAddress - _address + 1;
|
|
|
|
if ((diffEnd / stride < 5))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
if (m_pCurrent != nullptr) {
|
|
|
|
m_pCurrent->m_originX = m_pCurrent->m_originY = 0;
|
|
|
|
if ((m_pCurrent->m_startAddress != _address)) {
|
|
|
|
if (isSubBuffer(m_pCurrent)) {
|
|
|
|
const u32 stride = _width << _size >> 1;
|
2022-01-16 17:27:17 +00:00
|
|
|
const word addrOffset = _address - m_pCurrent->m_startAddress;
|
2018-09-25 09:37:57 +00:00
|
|
|
m_pCurrent->m_originX = (addrOffset % stride) >> (_size - 1);
|
|
|
|
m_pCurrent->m_originY = addrOffset / stride;
|
|
|
|
gSP.changed |= CHANGED_VIEWPORT;
|
|
|
|
gDP.changed |= CHANGED_SCISSOR;
|
|
|
|
return;
|
|
|
|
} else if (isOverlappingBuffer(m_pCurrent)) {
|
|
|
|
m_pCurrent->m_endAddress = _address - 1;
|
|
|
|
m_pCurrent = nullptr;
|
|
|
|
} else {
|
|
|
|
removeBuffer(m_pCurrent->m_startAddress);
|
|
|
|
m_pCurrent = nullptr;
|
|
|
|
}
|
|
|
|
} else if ((m_pCurrent->m_width != _width) ||
|
|
|
|
(m_pCurrent->m_size < _size) ||
|
|
|
|
(m_pCurrent->m_scale != scaleX)) {
|
2014-09-08 11:01:22 +00:00
|
|
|
removeBuffer(m_pCurrent->m_startAddress);
|
2016-06-10 06:06:06 +00:00
|
|
|
m_pCurrent = nullptr;
|
2013-09-05 15:53:49 +00:00
|
|
|
} else {
|
2015-02-09 12:27:39 +00:00
|
|
|
m_pCurrent->m_resolved = false;
|
2017-01-20 11:47:19 +00:00
|
|
|
gfxContext.bindFramebuffer(bufferTarget::FRAMEBUFFER, m_pCurrent->m_FBO);
|
2014-09-08 11:01:22 +00:00
|
|
|
if (m_pCurrent->m_size != _size) {
|
2013-09-05 15:53:49 +00:00
|
|
|
f32 fillColor[4];
|
|
|
|
gDPGetFillColor(fillColor);
|
2017-01-15 07:57:25 +00:00
|
|
|
wnd.getDrawer().clearColorBuffer(fillColor);
|
2014-09-08 11:01:22 +00:00
|
|
|
m_pCurrent->m_size = _size;
|
|
|
|
m_pCurrent->m_pTexture->format = _format;
|
|
|
|
m_pCurrent->m_pTexture->size = _size;
|
2016-06-10 06:06:06 +00:00
|
|
|
if (m_pCurrent->m_pResolveTexture != nullptr) {
|
2015-02-21 14:20:32 +00:00
|
|
|
m_pCurrent->m_pResolveTexture->format = _format;
|
|
|
|
m_pCurrent->m_pResolveTexture->size = _size;
|
|
|
|
}
|
2015-04-29 13:58:06 +00:00
|
|
|
if (m_pCurrent->m_copiedToRdram)
|
|
|
|
m_pCurrent->copyRdram();
|
2013-09-05 15:53:49 +00:00
|
|
|
}
|
2013-04-05 06:13:26 +00:00
|
|
|
}
|
2013-09-05 15:53:49 +00:00
|
|
|
}
|
2016-06-10 06:06:06 +00:00
|
|
|
const bool bNew = m_pCurrent == nullptr;
|
2013-10-19 13:32:39 +00:00
|
|
|
if (bNew) {
|
2013-09-05 15:53:49 +00:00
|
|
|
// Wasn't found or removed, create a new one
|
2014-09-08 11:01:22 +00:00
|
|
|
m_list.emplace_front();
|
|
|
|
FrameBuffer & buffer = m_list.front();
|
2017-03-02 15:09:08 +00:00
|
|
|
buffer.init(_address, _format, _size, _width, _cfb);
|
2014-09-08 11:01:22 +00:00
|
|
|
m_pCurrent = &buffer;
|
2018-09-15 15:46:15 +00:00
|
|
|
RDRAMtoColorBuffer::get().copyFromRDRAM(m_pCurrent);
|
2018-12-09 09:24:35 +00:00
|
|
|
if (_cfb)
|
|
|
|
m_pCurrent->copyRdram();
|
2013-06-04 16:05:09 +00:00
|
|
|
}
|
2013-06-01 13:10:30 +00:00
|
|
|
|
2015-04-08 17:06:17 +00:00
|
|
|
if (_address == gDP.depthImageAddress)
|
|
|
|
depthBufferList().saveBuffer(_address);
|
|
|
|
else
|
|
|
|
attachDepthBuffer();
|
2013-06-01 13:10:30 +00:00
|
|
|
|
2017-05-25 08:54:30 +00:00
|
|
|
DebugMsg( DEBUG_NORMAL, "FrameBuffer_SaveBuffer( 0x%08X )\n", _address);
|
2015-03-15 05:31:43 +00:00
|
|
|
|
2016-12-08 16:29:08 +00:00
|
|
|
if (m_pCurrent->isAuxiliary() &&
|
|
|
|
m_pCurrent->m_pDepthBuffer != nullptr &&
|
|
|
|
bPrevIsDepth &&
|
|
|
|
(config.generalEmulation.hacks&hack_LoadDepthTextures) == 0) {
|
2016-09-11 11:08:03 +00:00
|
|
|
// N64 games may use partial depth buffer clear for aux buffers
|
|
|
|
// It will not work for GL, so we have to force clear depth buffer for aux buffer
|
2018-09-27 11:31:41 +00:00
|
|
|
wnd.getDrawer().clearDepthBuffer();
|
2016-09-11 11:08:03 +00:00
|
|
|
}
|
2016-09-08 15:29:12 +00:00
|
|
|
|
2015-03-09 11:17:52 +00:00
|
|
|
m_pCurrent->m_isDepthBuffer = _address == gDP.depthImageAddress;
|
2015-03-09 12:17:45 +00:00
|
|
|
m_pCurrent->m_isPauseScreen = m_pCurrent->m_isOBScreen = false;
|
2018-04-16 19:13:18 +00:00
|
|
|
m_pCurrent->m_copied = false;
|
2018-09-25 09:37:57 +00:00
|
|
|
m_pCurrent->m_swapCount = wnd.getBuffersSwapCount();
|
2013-04-05 06:13:26 +00:00
|
|
|
}
|
|
|
|
|
2015-10-31 02:36:16 +00:00
|
|
|
void FrameBufferList::copyAux()
|
|
|
|
{
|
2017-01-20 11:47:19 +00:00
|
|
|
for (auto iter = m_list.begin(); iter != m_list.end(); ++iter) {
|
2017-02-27 10:32:20 +00:00
|
|
|
if (iter->isAuxiliary())
|
2015-10-31 03:11:33 +00:00
|
|
|
FrameBuffer_CopyToRDRAM(iter->m_startAddress, true);
|
2015-10-31 02:36:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FrameBufferList::removeAux()
|
|
|
|
{
|
2017-01-20 11:47:19 +00:00
|
|
|
for (auto iter = m_list.begin(); iter != m_list.end(); ++iter) {
|
2017-02-27 10:32:20 +00:00
|
|
|
while (iter->isAuxiliary()) {
|
2015-10-31 02:36:16 +00:00
|
|
|
if (&(*iter) == m_pCurrent) {
|
2016-06-10 06:06:06 +00:00
|
|
|
m_pCurrent = nullptr;
|
2018-04-03 15:08:06 +00:00
|
|
|
gfxContext.bindFramebuffer(bufferTarget::DRAW_FRAMEBUFFER, ObjectHandle::defaultFramebuffer);
|
2015-10-31 02:36:16 +00:00
|
|
|
}
|
|
|
|
iter = m_list.erase(iter);
|
|
|
|
if (iter == m_list.end())
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-08 11:01:22 +00:00
|
|
|
void FrameBufferList::removeBuffer(u32 _address )
|
|
|
|
{
|
2017-01-20 11:47:19 +00:00
|
|
|
for (auto iter = m_list.begin(); iter != m_list.end(); ++iter)
|
2014-09-08 11:01:22 +00:00
|
|
|
if (iter->m_startAddress == _address) {
|
2015-09-24 12:06:07 +00:00
|
|
|
if (&(*iter) == m_pCurrent) {
|
2016-06-10 06:06:06 +00:00
|
|
|
m_pCurrent = nullptr;
|
2018-04-03 15:08:06 +00:00
|
|
|
gfxContext.bindFramebuffer(bufferTarget::DRAW_FRAMEBUFFER, ObjectHandle::defaultFramebuffer);
|
2015-09-24 12:06:07 +00:00
|
|
|
}
|
2014-09-08 11:01:22 +00:00
|
|
|
m_list.erase(iter);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-11 14:46:00 +00:00
|
|
|
void FrameBufferList::removeBuffers(u32 _width)
|
2015-03-11 11:33:47 +00:00
|
|
|
{
|
2016-06-10 06:06:06 +00:00
|
|
|
m_pCurrent = nullptr;
|
2017-01-20 11:47:19 +00:00
|
|
|
for (auto iter = m_list.begin(); iter != m_list.end(); ++iter) {
|
2015-03-11 14:46:00 +00:00
|
|
|
while (iter->m_width == _width) {
|
2015-09-24 12:06:07 +00:00
|
|
|
if (&(*iter) == m_pCurrent) {
|
2016-06-10 06:06:06 +00:00
|
|
|
m_pCurrent = nullptr;
|
2018-04-03 15:08:06 +00:00
|
|
|
gfxContext.bindFramebuffer(bufferTarget::DRAW_FRAMEBUFFER, ObjectHandle::defaultFramebuffer);
|
2015-09-24 12:06:07 +00:00
|
|
|
}
|
2015-03-11 11:33:47 +00:00
|
|
|
iter = m_list.erase(iter);
|
|
|
|
if (iter == m_list.end())
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-29 10:01:59 +00:00
|
|
|
void FrameBufferList::depthBufferCopyRdram()
|
|
|
|
{
|
|
|
|
FrameBuffer * pCurrentDepthBuffer = findBuffer(gDP.depthImageAddress);
|
|
|
|
if (pCurrentDepthBuffer != nullptr)
|
|
|
|
pCurrentDepthBuffer->copyRdram();
|
|
|
|
}
|
|
|
|
|
2016-02-27 05:58:56 +00:00
|
|
|
void FrameBufferList::fillBufferInfo(void * _pinfo, u32 _size)
|
2015-11-23 12:36:03 +00:00
|
|
|
{
|
2016-02-27 05:58:56 +00:00
|
|
|
FBInfo::FrameBufferInfo* pInfo = reinterpret_cast<FBInfo::FrameBufferInfo*>(_pinfo);
|
|
|
|
|
2015-11-23 12:36:03 +00:00
|
|
|
u32 idx = 0;
|
2017-01-20 11:47:19 +00:00
|
|
|
for (auto iter = m_list.begin(); iter != m_list.end(); ++iter) {
|
2016-01-17 10:07:45 +00:00
|
|
|
if (iter->m_width == VI.width && !iter->m_cfb && !iter->m_isDepthBuffer) {
|
2016-02-27 05:58:56 +00:00
|
|
|
pInfo[idx].addr = iter->m_startAddress;
|
|
|
|
pInfo[idx].width = iter->m_width;
|
|
|
|
pInfo[idx].height = iter->m_height;
|
|
|
|
pInfo[idx++].size = iter->m_size;
|
2015-11-23 12:36:03 +00:00
|
|
|
if (idx >= _size)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-08 11:01:22 +00:00
|
|
|
void FrameBufferList::attachDepthBuffer()
|
2013-10-30 06:07:24 +00:00
|
|
|
{
|
2017-02-25 06:38:33 +00:00
|
|
|
FrameBuffer * pCurrent = config.frameBufferEmulation.enable == 0 ? &m_list.back() : m_pCurrent;
|
|
|
|
if (pCurrent == nullptr)
|
2015-01-27 16:00:06 +00:00
|
|
|
return;
|
|
|
|
|
2014-09-08 11:00:13 +00:00
|
|
|
DepthBuffer * pDepthBuffer = depthBufferList().getCurrent();
|
2017-02-03 14:28:06 +00:00
|
|
|
|
2017-02-25 06:38:33 +00:00
|
|
|
if (pCurrent->m_FBO.isNotNull() && pDepthBuffer != nullptr) {
|
|
|
|
pDepthBuffer->initDepthImageTexture(pCurrent);
|
|
|
|
pDepthBuffer->initDepthBufferTexture(pCurrent);
|
2017-01-25 09:57:38 +00:00
|
|
|
|
|
|
|
bool goodDepthBufferTexture = false;
|
2018-04-06 17:17:04 +00:00
|
|
|
if (Context::DepthFramebufferTextures) {
|
2019-01-09 11:19:01 +00:00
|
|
|
if (Context::WeakBlitFramebuffer)
|
2019-05-08 14:04:24 +00:00
|
|
|
goodDepthBufferTexture = pDepthBuffer->m_pDepthBufferTexture->width == pCurrent->m_pTexture->width;
|
2019-01-09 11:19:01 +00:00
|
|
|
else
|
2019-05-08 14:04:24 +00:00
|
|
|
goodDepthBufferTexture = pDepthBuffer->m_pDepthBufferTexture->width >= pCurrent->m_pTexture->width ||
|
2020-11-14 09:54:28 +00:00
|
|
|
std::abs(static_cast<s32>(pCurrent->m_width) - static_cast<s32>(pDepthBuffer->m_width)) < 2;
|
2017-01-25 09:57:38 +00:00
|
|
|
} else {
|
2019-05-08 14:04:24 +00:00
|
|
|
goodDepthBufferTexture = pDepthBuffer->m_depthRenderbufferWidth == pCurrent->m_pTexture->width;
|
2017-01-25 09:57:38 +00:00
|
|
|
}
|
|
|
|
|
2017-01-20 11:47:19 +00:00
|
|
|
if (goodDepthBufferTexture) {
|
2017-02-25 06:38:33 +00:00
|
|
|
pCurrent->m_pDepthBuffer = pDepthBuffer;
|
|
|
|
pDepthBuffer->setDepthAttachment(pCurrent->m_FBO, bufferTarget::DRAW_FRAMEBUFFER);
|
2020-02-09 06:02:55 +00:00
|
|
|
if (config.frameBufferEmulation.N64DepthCompare != Config::dcDisable)
|
2018-04-12 15:15:45 +00:00
|
|
|
pDepthBuffer->bindDepthImageTexture(pCurrent->m_FBO);
|
2015-04-11 17:17:38 +00:00
|
|
|
} else
|
2017-02-25 06:38:33 +00:00
|
|
|
pCurrent->m_pDepthBuffer = nullptr;
|
2015-01-27 16:00:06 +00:00
|
|
|
} else
|
2017-02-25 06:38:33 +00:00
|
|
|
pCurrent->m_pDepthBuffer = nullptr;
|
2015-01-27 16:00:06 +00:00
|
|
|
|
2017-01-22 13:43:05 +00:00
|
|
|
assert(!gfxContext.isFramebufferError());
|
2013-10-30 06:07:24 +00:00
|
|
|
}
|
2013-06-01 13:13:04 +00:00
|
|
|
|
2016-03-06 15:12:23 +00:00
|
|
|
void FrameBufferList::clearDepthBuffer(DepthBuffer * _pDepthBuffer)
|
|
|
|
{
|
2017-01-20 11:47:19 +00:00
|
|
|
for (auto iter = m_list.begin(); iter != m_list.end(); ++iter) {
|
2016-03-06 15:12:23 +00:00
|
|
|
if (iter->m_pDepthBuffer == _pDepthBuffer) {
|
2016-06-10 06:06:06 +00:00
|
|
|
iter->m_pDepthBuffer = nullptr;
|
2016-03-06 15:12:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-08 11:01:22 +00:00
|
|
|
void FrameBuffer_Init()
|
|
|
|
{
|
|
|
|
frameBufferList().init();
|
2015-05-04 08:46:14 +00:00
|
|
|
if (config.frameBufferEmulation.enable != 0) {
|
2016-05-28 15:37:20 +00:00
|
|
|
ColorBufferToRDRAM::get().init();
|
|
|
|
DepthBufferToRDRAM::get().init();
|
|
|
|
RDRAMtoColorBuffer::get().init();
|
2015-05-04 08:46:14 +00:00
|
|
|
}
|
2014-09-08 11:01:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void FrameBuffer_Destroy()
|
|
|
|
{
|
2016-05-28 15:37:20 +00:00
|
|
|
RDRAMtoColorBuffer::get().destroy();
|
|
|
|
DepthBufferToRDRAM::get().destroy();
|
2016-11-14 03:33:23 +00:00
|
|
|
ColorBufferToRDRAM::get().destroy();
|
2014-09-08 11:01:22 +00:00
|
|
|
frameBufferList().destroy();
|
|
|
|
}
|
|
|
|
|
2017-02-25 06:38:33 +00:00
|
|
|
void FrameBufferList::_renderScreenSizeBuffer()
|
|
|
|
{
|
|
|
|
if (m_list.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
DisplayWindow & wnd = dwnd();
|
|
|
|
GraphicsDrawer & drawer = wnd.getDrawer();
|
|
|
|
FrameBuffer *pBuffer = &m_list.back();
|
|
|
|
PostProcessor & postProcessor = PostProcessor::get();
|
2018-09-09 09:34:00 +00:00
|
|
|
FrameBuffer * pFilteredBuffer = pBuffer;
|
|
|
|
for (const auto & f : postProcessor.getPostprocessingList())
|
|
|
|
pFilteredBuffer = f(postProcessor, pFilteredBuffer);
|
2017-02-25 06:38:33 +00:00
|
|
|
CachedTexture * pBufferTexture = pFilteredBuffer->m_pTexture;
|
|
|
|
|
2018-01-05 08:27:19 +00:00
|
|
|
const u32 wndWidth = wnd.getWidth();
|
|
|
|
const u32 wndHeight = wnd.getHeight();
|
|
|
|
s32 srcCoord[4] = { 0, 0, static_cast<s32>(wndWidth), static_cast<s32>(wndHeight) };
|
2017-02-25 06:38:33 +00:00
|
|
|
|
2018-01-05 08:27:19 +00:00
|
|
|
const u32 screenWidth = wnd.getScreenWidth();
|
|
|
|
const u32 screenHeight = wnd.getScreenHeight();
|
|
|
|
const u32 wndHeightOffset = wnd.getHeightOffset();
|
|
|
|
const s32 hOffset = (screenWidth - wndWidth) / 2;
|
|
|
|
const s32 vOffset = (screenHeight - wndHeight) / 2 + wndHeightOffset;
|
|
|
|
s32 dstCoord[4] = { hOffset, vOffset, hOffset + static_cast<s32>(wndWidth), vOffset + static_cast<s32>(wndHeight) };
|
2017-02-25 06:38:33 +00:00
|
|
|
|
2018-04-03 15:08:06 +00:00
|
|
|
gfxContext.bindFramebuffer(bufferTarget::DRAW_FRAMEBUFFER, ObjectHandle::defaultFramebuffer);
|
2017-02-25 06:38:33 +00:00
|
|
|
|
2018-05-05 07:45:18 +00:00
|
|
|
gfxContext.clearColorBuffer(0.0f, 0.0f, 0.0f, 0.0f);
|
2017-02-25 06:38:33 +00:00
|
|
|
|
|
|
|
GraphicsDrawer::BlitOrCopyRectParams blitParams;
|
|
|
|
blitParams.srcX0 = srcCoord[0];
|
2017-02-27 09:34:10 +00:00
|
|
|
blitParams.srcY0 = srcCoord[3];
|
2017-02-25 06:38:33 +00:00
|
|
|
blitParams.srcX1 = srcCoord[2];
|
2017-02-27 09:34:10 +00:00
|
|
|
blitParams.srcY1 = srcCoord[1];
|
2018-01-05 08:27:19 +00:00
|
|
|
blitParams.srcWidth = wndWidth;
|
|
|
|
blitParams.srcHeight = wndHeight;
|
2017-02-25 06:38:33 +00:00
|
|
|
blitParams.dstX0 = dstCoord[0];
|
|
|
|
blitParams.dstY0 = dstCoord[1];
|
|
|
|
blitParams.dstX1 = dstCoord[2];
|
|
|
|
blitParams.dstY1 = dstCoord[3];
|
2018-01-05 08:27:19 +00:00
|
|
|
blitParams.dstWidth = screenWidth;
|
|
|
|
blitParams.dstHeight = screenHeight + wndHeightOffset;
|
2020-07-28 07:24:01 +00:00
|
|
|
const bool downscale = blitParams.srcWidth >= blitParams.dstWidth || blitParams.srcHeight >= blitParams.dstHeight;
|
|
|
|
blitParams.filter = downscale || config.generalEmulation.enableHybridFilter > 0 ?
|
2020-05-10 02:12:08 +00:00
|
|
|
textureParameters::FILTER_LINEAR :
|
2020-07-28 07:24:01 +00:00
|
|
|
textureParameters::FILTER_NEAREST; //upscale; hybridFilter disabled
|
2017-02-25 06:38:33 +00:00
|
|
|
blitParams.mask = blitMask::COLOR_BUFFER;
|
|
|
|
blitParams.tex[0] = pBufferTexture;
|
2020-05-05 10:20:25 +00:00
|
|
|
blitParams.combiner = downscale ? CombinerInfo::get().getTexrectDownscaleCopyProgram() :
|
|
|
|
CombinerInfo::get().getTexrectUpscaleCopyProgram();
|
2017-02-25 06:38:33 +00:00
|
|
|
blitParams.readBuffer = pFilteredBuffer->m_FBO;
|
|
|
|
|
|
|
|
drawer.blitOrCopyTexturedRect(blitParams);
|
|
|
|
|
2018-04-03 15:08:06 +00:00
|
|
|
gfxContext.bindFramebuffer(bufferTarget::READ_FRAMEBUFFER, ObjectHandle::defaultFramebuffer);
|
2017-02-25 06:38:33 +00:00
|
|
|
|
|
|
|
wnd.swapBuffers();
|
|
|
|
gfxContext.bindFramebuffer(bufferTarget::DRAW_FRAMEBUFFER, pBuffer->m_FBO);
|
2018-05-01 12:21:30 +00:00
|
|
|
if (config.frameBufferEmulation.forceDepthBufferClear != 0) {
|
2018-09-27 11:31:41 +00:00
|
|
|
drawer.clearDepthBuffer();
|
2018-05-01 12:21:30 +00:00
|
|
|
}
|
2017-02-25 06:38:33 +00:00
|
|
|
gDP.changed |= CHANGED_SCISSOR;
|
|
|
|
}
|
|
|
|
|
2019-01-09 14:54:31 +00:00
|
|
|
void FrameBufferList::RdpUpdate::init()
|
|
|
|
{
|
|
|
|
oldvstart = 0U;
|
|
|
|
prevvicurrent = 0U;
|
|
|
|
prevwasblank = false;
|
|
|
|
prevserrate = false;
|
|
|
|
oldlowerfield = false;
|
|
|
|
emucontrolsvicurrent = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This function was taken from angrylion's code and adopted for my needs */
|
|
|
|
bool FrameBufferList::RdpUpdate::update(RdpUpdateResult & _result)
|
2013-06-01 13:13:04 +00:00
|
|
|
{
|
2019-01-20 11:44:54 +00:00
|
|
|
static const s32 PRESCALE_WIDTH = 640U;
|
|
|
|
static const s32 PRESCALE_HEIGHT = 625U;
|
2017-03-01 16:12:39 +00:00
|
|
|
|
2019-01-20 11:44:54 +00:00
|
|
|
const s32 x_add = _SHIFTR(*REG.VI_X_SCALE, 0, 12);
|
|
|
|
const s32 y_add = _SHIFTR(*REG.VI_Y_SCALE, 0, 12);
|
2017-03-01 16:12:39 +00:00
|
|
|
const u32 v_sync = _SHIFTR(*REG.VI_V_SYNC, 0, 10);
|
|
|
|
const bool ispal = (v_sync > 550);
|
2019-01-28 00:56:30 +00:00
|
|
|
const s32 x1 = _SHIFTR( *REG.VI_H_START, 16, 10 );
|
|
|
|
const s32 y1 = _SHIFTR( *REG.VI_V_START, 16, 10 );
|
|
|
|
const s32 x2 = _SHIFTR( *REG.VI_H_START, 0, 10 );
|
|
|
|
const s32 y2 = _SHIFTR( *REG.VI_V_START, 0, 10 );
|
2017-03-01 16:12:39 +00:00
|
|
|
|
2019-01-28 00:56:30 +00:00
|
|
|
const s32 delta_x = x2 - x1;
|
|
|
|
const s32 delta_y = y2 - y1;
|
2017-03-01 16:12:39 +00:00
|
|
|
const u32 vitype = _SHIFTR( *REG.VI_STATUS, 0, 2 );
|
2014-09-26 12:19:23 +00:00
|
|
|
|
2020-10-21 17:34:08 +00:00
|
|
|
const bool serration_pulses = (*REG.VI_STATUS & VI_STATUS_SERRATE_ENABLED) != 0;
|
2019-01-09 14:54:31 +00:00
|
|
|
const bool validinterlace = ((vitype & 2) != 0 ) && serration_pulses;
|
|
|
|
if (validinterlace && prevserrate && emucontrolsvicurrent < 0)
|
|
|
|
emucontrolsvicurrent = (*REG.VI_V_CURRENT_LINE & 1) != prevvicurrent ? 1 : 0;
|
|
|
|
|
|
|
|
bool lowerfield = 0;
|
|
|
|
if (validinterlace) {
|
|
|
|
if (emucontrolsvicurrent == 1)
|
|
|
|
lowerfield = (*REG.VI_V_CURRENT_LINE & 1) == 0;
|
|
|
|
else if (!emucontrolsvicurrent) {
|
|
|
|
if (y1 == oldvstart)
|
|
|
|
lowerfield = !oldlowerfield;
|
|
|
|
else
|
|
|
|
lowerfield = y1 < oldvstart;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
oldlowerfield = lowerfield;
|
|
|
|
|
|
|
|
if (validinterlace) {
|
|
|
|
prevserrate = true;
|
|
|
|
prevvicurrent = *REG.VI_V_CURRENT_LINE & 1;
|
|
|
|
oldvstart = y1;
|
|
|
|
} else
|
|
|
|
prevserrate = false;
|
2017-03-01 16:12:39 +00:00
|
|
|
|
2019-01-20 11:44:54 +00:00
|
|
|
s32 hres = delta_x;
|
|
|
|
s32 vres = delta_y;
|
2017-03-01 16:12:39 +00:00
|
|
|
s32 h_start = x1 - (ispal ? 128 : 108);
|
2019-01-09 14:54:31 +00:00
|
|
|
s32 v_start = (y1 - (ispal ? 44 : 34)) / 2;
|
2020-11-14 09:54:28 +00:00
|
|
|
s32 x_start = _SHIFTR(*REG.VI_X_SCALE, 16, 12);
|
|
|
|
s32 y_start = _SHIFTR(*REG.VI_Y_SCALE, 16, 12);
|
2017-03-01 16:12:39 +00:00
|
|
|
|
2019-01-09 14:54:31 +00:00
|
|
|
bool h_start_clamped = h_start < 0;
|
2017-03-01 16:12:39 +00:00
|
|
|
if (h_start < 0) {
|
|
|
|
x_start -= x_add * h_start;
|
2019-01-09 14:54:31 +00:00
|
|
|
hres += h_start;
|
|
|
|
|
2017-03-01 16:12:39 +00:00
|
|
|
h_start = 0;
|
|
|
|
}
|
2019-01-09 14:54:31 +00:00
|
|
|
|
|
|
|
if (v_start < 0) {
|
2020-11-14 09:54:28 +00:00
|
|
|
y_start += (y_add * (-v_start));
|
2019-01-09 14:54:31 +00:00
|
|
|
v_start = 0;
|
|
|
|
}
|
|
|
|
|
2017-03-01 16:12:39 +00:00
|
|
|
vres >>= 1;
|
|
|
|
|
2019-01-09 14:54:31 +00:00
|
|
|
const bool hres_clamped = hres + h_start > PRESCALE_WIDTH;
|
|
|
|
if (hres_clamped)
|
2017-03-01 16:12:39 +00:00
|
|
|
hres = PRESCALE_WIDTH - h_start;
|
2019-01-09 14:54:31 +00:00
|
|
|
if (vres + v_start > PRESCALE_HEIGHT)
|
2017-03-01 16:12:39 +00:00
|
|
|
vres = PRESCALE_HEIGHT - v_start;
|
|
|
|
|
2020-11-14 09:54:28 +00:00
|
|
|
s32 vactivelines = static_cast<s32>(v_sync - (ispal ? 44 : 34));
|
2017-03-01 16:12:39 +00:00
|
|
|
if (vactivelines > PRESCALE_HEIGHT) {
|
2019-04-10 04:29:06 +00:00
|
|
|
LOG(LOG_VERBOSE, "VI_V_SYNC_REG too big");
|
2017-03-01 16:12:39 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vactivelines < 0) {
|
2019-04-10 04:29:06 +00:00
|
|
|
LOG(LOG_VERBOSE, "vactivelines lesser than 0");
|
2017-03-01 16:12:39 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hres <= 0 || vres <= 0 || ((vitype & 2) == 0 && prevwasblank)) /* early return. */
|
|
|
|
return false;
|
|
|
|
|
2019-01-09 14:54:31 +00:00
|
|
|
if ((vitype & 2) == 0) {
|
2017-03-01 16:12:39 +00:00
|
|
|
prevwasblank = true;
|
2022-02-08 13:01:30 +00:00
|
|
|
#ifdef NATIVE
|
|
|
|
//return false;//HACK, REG.VI_STATUS might be set up incorrectly
|
|
|
|
#else
|
|
|
|
return false
|
|
|
|
#endif
|
2017-03-01 16:12:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
prevwasblank = false;
|
|
|
|
|
2020-11-14 09:54:28 +00:00
|
|
|
_result.vi_hres = static_cast<u32>(hres);
|
|
|
|
_result.vi_vres = static_cast<u32>(vres);
|
2017-03-01 16:12:39 +00:00
|
|
|
_result.vi_ispal = ispal;
|
2020-11-14 09:54:28 +00:00
|
|
|
_result.vi_h_start = static_cast<u32>(h_start);
|
|
|
|
_result.vi_v_start = static_cast<u32>(v_start);
|
|
|
|
_result.vi_x_start = static_cast<u32>(x_start);
|
|
|
|
_result.vi_y_start = static_cast<u32>(y_start);
|
|
|
|
_result.vi_x_add = static_cast<u32>(x_add);
|
|
|
|
_result.vi_y_add = static_cast<u32>(y_add);
|
2019-01-09 14:54:31 +00:00
|
|
|
_result.vi_minhpass = h_start_clamped ? 0 : 8;
|
|
|
|
_result.vi_maxhpass = hres_clamped ? 0 : 7;
|
2017-03-01 16:12:39 +00:00
|
|
|
_result.vi_width = _SHIFTR(*REG.VI_WIDTH, 0, 12);
|
|
|
|
_result.vi_lowerfield = lowerfield;
|
2022-02-08 15:58:56 +00:00
|
|
|
#ifndef NATIVE
|
|
|
|
_result.vi_origin = _SHIFTR(*REG.VI_ORIGIN, 0, 24);
|
|
|
|
#else
|
2022-02-08 02:44:17 +00:00
|
|
|
_result.vi_origin = *REG.VI_ORIGIN;//This is incorrect REG.VI_ORIGIN should contain only 24 bits of the frame buffer address
|
2022-02-08 15:58:56 +00:00
|
|
|
#endif
|
2017-03-01 16:12:39 +00:00
|
|
|
_result.vi_fsaa = (*REG.VI_STATUS & 512) == 0;
|
2020-10-21 17:34:08 +00:00
|
|
|
_result.vi_divot = (*REG.VI_STATUS & VI_STATUS_DIVOT_ENABLED) != 0;
|
2017-03-01 16:12:39 +00:00
|
|
|
return true;
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
{
|
|
|
|
int pixels;
|
|
|
|
int prevy, y_start;
|
|
|
|
int cur_x, line_x;
|
|
|
|
register int i;
|
|
|
|
const int VI_width = *GET_GFX_INFO(VI_WIDTH) & 0x00000FFF;
|
|
|
|
const int x_add = *GET_GFX_INFO(VI_X_SCALE) & 0x00000FFF;
|
|
|
|
const int y_add = *GET_GFX_INFO(VI_Y_SCALE) & 0x00000FFF;
|
|
|
|
|
|
|
|
y_start = *GET_GFX_INFO(VI_Y_SCALE) >> 16 & 0x0FFF;
|
|
|
|
|
|
|
|
//while (--vres >= 0)
|
|
|
|
{
|
|
|
|
x_start = *GET_GFX_INFO(VI_X_SCALE) >> 16 & 0x0FFF;
|
|
|
|
prescale_ptr += line_count;
|
|
|
|
|
|
|
|
prevy = y_start >> 10;
|
|
|
|
pixels = VI_width * prevy;
|
|
|
|
|
|
|
|
//for (i = 0; i < hres; i++)
|
|
|
|
{
|
|
|
|
unsigned long pix;
|
|
|
|
unsigned long addr;
|
|
|
|
|
|
|
|
line_x = x_start >> 10;
|
|
|
|
cur_x = pixels + line_x;
|
|
|
|
|
|
|
|
x_start += x_add;
|
|
|
|
addr = frame_buffer + 4 * cur_x;
|
|
|
|
pix = *(int32_t *)(RDRAM + addr);
|
|
|
|
}
|
|
|
|
y_start += y_add;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2018-05-05 07:45:18 +00:00
|
|
|
s32 FrameBufferList::OverscanBuffer::getHOffset() const
|
|
|
|
{
|
|
|
|
if (m_enabled)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return m_hOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 FrameBufferList::OverscanBuffer::getVOffset() const
|
|
|
|
{
|
|
|
|
if (m_enabled)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return m_vOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
f32 FrameBufferList::OverscanBuffer::getScaleX() const
|
|
|
|
{
|
2018-08-28 13:57:50 +00:00
|
|
|
if (m_enabled)
|
|
|
|
return m_scale;
|
|
|
|
return dwnd().getScaleX();
|
2018-05-05 07:45:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
f32 FrameBufferList::OverscanBuffer::getScaleY(u32 _fullHeight) const
|
|
|
|
{
|
|
|
|
if (m_enabled)
|
|
|
|
return m_scale;
|
|
|
|
|
2020-11-14 09:54:28 +00:00
|
|
|
return static_cast<f32>(dwnd().getHeight()) / static_cast<f32>(_fullHeight);
|
2018-05-05 07:45:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void FrameBufferList::OverscanBuffer::init()
|
|
|
|
{
|
|
|
|
m_enabled = config.frameBufferEmulation.enableOverscan != 0;
|
|
|
|
if (m_enabled)
|
|
|
|
m_FBO = gfxContext.createFramebuffer();
|
|
|
|
|
|
|
|
DisplayWindow & wnd = dwnd();
|
|
|
|
m_hOffset = (wnd.getScreenWidth() - wnd.getWidth()) / 2;
|
|
|
|
m_vOffset = (wnd.getScreenHeight() - wnd.getHeight()) / 2;
|
|
|
|
m_scale = wnd.getScaleX();
|
|
|
|
m_drawingWidth = wnd.getWidth();
|
|
|
|
m_bufferWidth = wnd.getScreenWidth();
|
|
|
|
m_bufferHeight = wnd.getScreenHeight() + wnd.getHeightOffset();
|
|
|
|
}
|
|
|
|
|
|
|
|
void FrameBufferList::OverscanBuffer::destroy()
|
|
|
|
{
|
|
|
|
gfxContext.deleteFramebuffer(m_FBO);
|
|
|
|
m_FBO = graphics::ObjectHandle::null;
|
|
|
|
textureCache().removeFrameBufferTexture(m_pTexture);
|
|
|
|
m_pTexture = nullptr;
|
2019-06-27 09:21:51 +00:00
|
|
|
textureCache().removeFrameBufferTexture(m_pDepthTexture);
|
|
|
|
m_pDepthTexture = nullptr;
|
|
|
|
#if defined(OS_WINDOWS)
|
|
|
|
gfxContext.bindFramebuffer(bufferTarget::DRAW_FRAMEBUFFER, ObjectHandle::defaultFramebuffer);
|
|
|
|
gfxContext.clearDepthBuffer();
|
|
|
|
#endif
|
2018-05-05 07:45:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void FrameBufferList::OverscanBuffer::setInputBuffer(const FrameBuffer * _pBuffer)
|
|
|
|
{
|
|
|
|
if (!m_enabled) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_pTexture != nullptr &&
|
|
|
|
m_pTexture->width == _pBuffer->m_pTexture->width &&
|
|
|
|
m_pTexture->height == _pBuffer->m_pTexture->height &&
|
|
|
|
m_scale == _pBuffer->m_scale) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
textureCache().removeFrameBufferTexture(m_pTexture);
|
2019-11-29 22:09:09 +00:00
|
|
|
m_pTexture = textureCache().addFrameBufferTexture(textureTarget::TEXTURE_2D);
|
2018-05-05 07:45:18 +00:00
|
|
|
const CachedTexture * pSrcTexture = _pBuffer->m_pTexture;
|
|
|
|
_initFrameBufferTexture(0,
|
|
|
|
_pBuffer->m_width,
|
|
|
|
VI_GetMaxBufferHeight(_pBuffer->m_width),
|
|
|
|
_pBuffer->m_scale,
|
|
|
|
pSrcTexture->format,
|
|
|
|
pSrcTexture->size,
|
|
|
|
m_pTexture);
|
|
|
|
_setAndAttachBufferTexture(m_FBO, m_pTexture, 0, false);
|
|
|
|
m_scale = _pBuffer->m_scale;
|
|
|
|
m_drawingWidth = m_bufferWidth = m_pTexture->width;
|
|
|
|
m_bufferHeight = m_pTexture->height;
|
2019-06-27 09:21:51 +00:00
|
|
|
|
|
|
|
if (config.frameBufferEmulation.copyDepthToMainDepthBuffer == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Init depth texture
|
|
|
|
textureCache().removeFrameBufferTexture(m_pDepthTexture);
|
2019-11-29 22:09:09 +00:00
|
|
|
m_pDepthTexture = textureCache().addFrameBufferTexture(textureTarget::TEXTURE_2D);
|
2019-06-27 09:21:51 +00:00
|
|
|
DepthBuffer::_initDepthBufferTexture(_pBuffer, m_pDepthTexture, false);
|
|
|
|
Context::FrameBufferRenderTarget params;
|
|
|
|
params.attachment = bufferAttachment::DEPTH_ATTACHMENT;
|
|
|
|
params.bufferHandle = m_FBO;
|
|
|
|
params.bufferTarget = bufferTarget::DRAW_FRAMEBUFFER;
|
|
|
|
params.textureHandle = m_pDepthTexture->name;
|
|
|
|
params.textureTarget = textureTarget::TEXTURE_2D;
|
|
|
|
gfxContext.addFrameBufferRenderTarget(params);
|
2018-05-05 07:45:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void FrameBufferList::OverscanBuffer::activate()
|
|
|
|
{
|
|
|
|
if (!m_enabled) {
|
|
|
|
gfxContext.bindFramebuffer(bufferTarget::DRAW_FRAMEBUFFER, ObjectHandle::defaultFramebuffer);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
gfxContext.bindFramebuffer(bufferTarget::DRAW_FRAMEBUFFER, m_FBO);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FrameBufferList::OverscanBuffer::draw(u32 _fullHeight, bool _PAL)
|
|
|
|
{
|
|
|
|
if (!m_enabled)
|
|
|
|
return;
|
|
|
|
|
|
|
|
DisplayWindow & wnd = dwnd();
|
|
|
|
GraphicsDrawer & drawer = wnd.getDrawer();
|
|
|
|
|
|
|
|
gfxContext.bindFramebuffer(bufferTarget::DRAW_FRAMEBUFFER, ObjectHandle::defaultFramebuffer);
|
2019-06-27 09:21:51 +00:00
|
|
|
#if defined(OS_WINDOWS)
|
|
|
|
gfxContext.clearDepthBuffer();
|
|
|
|
#endif
|
2018-05-05 07:45:18 +00:00
|
|
|
GraphicsDrawer::BlitOrCopyRectParams blitParams;
|
|
|
|
const auto & overscan = _PAL ? config.frameBufferEmulation.overscanPAL : config.frameBufferEmulation.overscanNTSC;
|
|
|
|
const s32 left = static_cast<s32>(overscan.left * m_scale);
|
|
|
|
const s32 right = static_cast<s32>(overscan.right * m_scale);
|
|
|
|
const s32 top = static_cast<s32>(overscan.top * m_scale);
|
|
|
|
const s32 bottom = static_cast<s32>(overscan.bottom * m_scale);
|
|
|
|
blitParams.srcX0 = left;
|
2019-01-11 13:20:10 +00:00
|
|
|
blitParams.srcY0 = static_cast<s32>(_fullHeight * m_scale) - bottom;
|
2020-11-14 09:54:28 +00:00
|
|
|
blitParams.srcX1 = static_cast<s32>(m_bufferWidth) - right;
|
2019-01-11 13:20:10 +00:00
|
|
|
blitParams.srcY1 = top;
|
2019-05-08 14:04:24 +00:00
|
|
|
blitParams.srcWidth = m_pTexture->width;
|
|
|
|
blitParams.srcHeight = m_pTexture->height;
|
2018-05-05 07:45:18 +00:00
|
|
|
blitParams.dstX0 = m_hOffset;
|
2020-11-14 09:54:28 +00:00
|
|
|
blitParams.dstY0 = m_vOffset + static_cast<s32>(wnd.getHeightOffset());
|
|
|
|
blitParams.dstX1 = m_hOffset + static_cast<s32>(wnd.getWidth());
|
|
|
|
blitParams.dstY1 = m_vOffset + static_cast<s32>(wnd.getHeight() + wnd.getHeightOffset());
|
2018-05-05 07:45:18 +00:00
|
|
|
blitParams.dstWidth = wnd.getScreenWidth();
|
|
|
|
blitParams.dstHeight = wnd.getScreenHeight() + wnd.getHeightOffset();
|
|
|
|
blitParams.mask = blitMask::COLOR_BUFFER;
|
|
|
|
blitParams.tex[0] = m_pTexture;
|
2020-07-28 07:24:01 +00:00
|
|
|
const bool downscale = blitParams.srcWidth >= blitParams.dstWidth || blitParams.srcHeight >= blitParams.dstHeight;
|
|
|
|
blitParams.filter = downscale || config.generalEmulation.enableHybridFilter > 0 ?
|
|
|
|
textureParameters::FILTER_LINEAR :
|
|
|
|
textureParameters::FILTER_NEAREST; //upscale; hybridFilter disabled
|
2019-06-27 09:21:51 +00:00
|
|
|
if (config.frameBufferEmulation.copyDepthToMainDepthBuffer != 0) {
|
|
|
|
blitParams.tex[1] = m_pDepthTexture;
|
2020-05-05 10:20:25 +00:00
|
|
|
blitParams.combiner = downscale ? CombinerInfo::get().getTexrectColorAndDepthDownscaleCopyProgram() :
|
|
|
|
CombinerInfo::get().getTexrectColorAndDepthUpscaleCopyProgram();
|
2019-09-29 09:13:03 +00:00
|
|
|
}
|
|
|
|
if (blitParams.combiner == nullptr) {
|
|
|
|
// copyDepthToMainDepthBuffer not set or not supported
|
2020-05-05 10:20:25 +00:00
|
|
|
blitParams.combiner = downscale ? CombinerInfo::get().getTexrectDownscaleCopyProgram() :
|
|
|
|
CombinerInfo::get().getTexrectUpscaleCopyProgram();
|
2019-06-27 09:21:51 +00:00
|
|
|
}
|
2020-05-05 10:20:25 +00:00
|
|
|
|
2018-05-05 07:45:18 +00:00
|
|
|
blitParams.readBuffer = m_FBO;
|
2019-01-11 13:20:10 +00:00
|
|
|
blitParams.invertY = false;
|
2018-05-05 07:45:18 +00:00
|
|
|
|
|
|
|
gfxContext.clearColorBuffer(0.0f, 0.0f, 0.0f, 0.0f);
|
2018-09-24 10:46:09 +00:00
|
|
|
// drawer.blitOrCopyTexturedRect(blitParams);
|
|
|
|
drawer.copyTexturedRect(blitParams);
|
2018-05-05 07:45:18 +00:00
|
|
|
}
|
2017-03-01 16:12:39 +00:00
|
|
|
|
|
|
|
void FrameBufferList::renderBuffer()
|
|
|
|
{
|
2017-06-16 08:36:07 +00:00
|
|
|
if (g_debugger.isDebugMode()) {
|
|
|
|
g_debugger.draw();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-02-25 06:38:33 +00:00
|
|
|
if (config.frameBufferEmulation.enable == 0) {
|
|
|
|
_renderScreenSizeBuffer();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-03-01 16:12:39 +00:00
|
|
|
RdpUpdateResult rdpRes;
|
2019-01-09 14:54:31 +00:00
|
|
|
if (!m_rdpUpdate.update(rdpRes)) {
|
2018-09-24 10:44:38 +00:00
|
|
|
gfxContext.bindFramebuffer(bufferTarget::DRAW_FRAMEBUFFER, ObjectHandle::defaultFramebuffer);
|
|
|
|
gfxContext.clearColorBuffer(0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
dwnd().swapBuffers();
|
|
|
|
if (m_pCurrent != nullptr)
|
|
|
|
gfxContext.bindFramebuffer(bufferTarget::DRAW_FRAMEBUFFER, m_pCurrent->m_FBO);
|
2017-03-01 16:12:39 +00:00
|
|
|
return;
|
2018-09-24 10:44:38 +00:00
|
|
|
}
|
2017-03-01 16:12:39 +00:00
|
|
|
|
|
|
|
FrameBuffer *pBuffer = findBuffer(rdpRes.vi_origin);
|
2016-06-10 06:06:06 +00:00
|
|
|
if (pBuffer == nullptr)
|
2013-09-04 02:15:05 +00:00
|
|
|
return;
|
2017-03-06 09:42:23 +00:00
|
|
|
pBuffer->m_isMainBuffer = true;
|
2018-05-05 07:45:18 +00:00
|
|
|
m_overscan.setInputBuffer(pBuffer);
|
2014-09-27 15:27:32 +00:00
|
|
|
|
2017-01-15 07:57:25 +00:00
|
|
|
DisplayWindow & wnd = dwnd();
|
|
|
|
GraphicsDrawer & drawer = wnd.getDrawer();
|
2017-03-01 16:12:39 +00:00
|
|
|
s32 srcY0, srcY1;
|
|
|
|
s32 dstX0, dstX1, dstY0, dstY1;
|
|
|
|
s32 srcWidth, srcHeight;
|
2018-06-23 14:56:10 +00:00
|
|
|
s32 XoffsetLeft = 0, XoffsetRight = 0;
|
2017-01-23 14:57:32 +00:00
|
|
|
s32 srcPartHeight = 0;
|
|
|
|
s32 dstPartHeight = 0;
|
2014-10-10 10:22:10 +00:00
|
|
|
|
2020-11-14 09:54:28 +00:00
|
|
|
dstY0 = static_cast<s32>(rdpRes.vi_v_start);
|
2017-03-01 16:12:39 +00:00
|
|
|
|
|
|
|
const u32 vFullHeight = rdpRes.vi_ispal ? 288 : 240;
|
2018-05-05 07:45:18 +00:00
|
|
|
const f32 dstScaleY = m_overscan.getScaleY(vFullHeight);
|
2014-10-10 10:22:10 +00:00
|
|
|
|
2022-01-16 17:27:17 +00:00
|
|
|
const word addrOffset = ((rdpRes.vi_origin - pBuffer->m_startAddress) << 1 >> pBuffer->m_size);
|
2020-11-14 09:54:28 +00:00
|
|
|
srcY0 = static_cast<s32>(addrOffset / pBuffer->m_width);
|
2017-04-12 08:32:01 +00:00
|
|
|
if ((addrOffset != 0) && (pBuffer->m_width == addrOffset * 2))
|
|
|
|
srcY0 = 1;
|
2014-09-26 12:19:23 +00:00
|
|
|
|
2017-03-01 16:12:39 +00:00
|
|
|
if ((rdpRes.vi_width != addrOffset * 2) && (addrOffset % rdpRes.vi_width != 0))
|
2020-11-14 09:54:28 +00:00
|
|
|
XoffsetRight = static_cast<s32>(rdpRes.vi_width - addrOffset % rdpRes.vi_width);
|
|
|
|
if (XoffsetRight == static_cast<s32>(pBuffer->m_width)) {
|
2018-06-23 14:56:10 +00:00
|
|
|
XoffsetRight = 0;
|
|
|
|
} else if (XoffsetRight > static_cast<s32>(pBuffer->m_width / 2)) {
|
|
|
|
XoffsetRight = 0;
|
2020-11-14 09:54:28 +00:00
|
|
|
XoffsetLeft = static_cast<s32>(addrOffset % rdpRes.vi_width);
|
2018-06-23 14:56:10 +00:00
|
|
|
}
|
2016-11-20 12:22:31 +00:00
|
|
|
|
2019-02-17 16:35:10 +00:00
|
|
|
if (!rdpRes.vi_lowerfield) {
|
|
|
|
if (srcY0 > 0 && (pBuffer->m_width > 320 || pBuffer->m_height > 240))
|
2017-03-01 16:12:39 +00:00
|
|
|
--srcY0;
|
2014-11-29 13:01:27 +00:00
|
|
|
if (dstY0 > 0)
|
|
|
|
--dstY0;
|
|
|
|
}
|
2014-09-26 12:19:23 +00:00
|
|
|
|
2019-01-12 16:28:52 +00:00
|
|
|
if ((config.generalEmulation.hacks & hack_LodeRunner) != 0) {
|
|
|
|
srcY0 = 1;
|
|
|
|
XoffsetRight = XoffsetLeft = 0;
|
|
|
|
}
|
|
|
|
|
2020-11-14 09:54:28 +00:00
|
|
|
srcWidth = static_cast<s32>(min(rdpRes.vi_width, (rdpRes.vi_hres * rdpRes.vi_x_add) >> 10));
|
|
|
|
srcHeight = static_cast<s32>(rdpRes.vi_width * ((rdpRes.vi_vres*rdpRes.vi_y_add + rdpRes.vi_y_start) >> 10) / pBuffer->m_width);
|
2017-03-01 16:12:39 +00:00
|
|
|
|
2017-03-13 14:45:21 +00:00
|
|
|
const u32 stride = pBuffer->m_width << pBuffer->m_size >> 1;
|
2018-09-07 16:00:45 +00:00
|
|
|
FrameBuffer *pNextBuffer = findBuffer(rdpRes.vi_origin + stride * min(u32(srcHeight) - 1, pBuffer->m_height - 1) - 1);
|
2017-03-13 14:45:21 +00:00
|
|
|
if (pNextBuffer == pBuffer)
|
|
|
|
pNextBuffer = nullptr;
|
|
|
|
|
|
|
|
if (pNextBuffer != nullptr) {
|
2014-10-10 10:22:10 +00:00
|
|
|
dstPartHeight = srcY0;
|
|
|
|
srcPartHeight = srcY0;
|
2017-03-01 16:12:39 +00:00
|
|
|
srcY1 = srcHeight;
|
2020-11-14 09:54:28 +00:00
|
|
|
dstY1 = dstY0 + static_cast<s32>(rdpRes.vi_vres) - dstPartHeight;
|
2014-09-27 15:27:32 +00:00
|
|
|
} else {
|
2020-11-14 09:54:28 +00:00
|
|
|
dstY1 = dstY0 + static_cast<s32>(rdpRes.vi_vres);
|
2017-03-01 16:12:39 +00:00
|
|
|
srcY1 = srcY0 + srcHeight;
|
2013-09-29 15:02:50 +00:00
|
|
|
}
|
2016-10-29 17:33:05 +00:00
|
|
|
PostProcessor & postProcessor = PostProcessor::get();
|
2018-09-09 09:34:00 +00:00
|
|
|
FrameBuffer * pFilteredBuffer = pBuffer;
|
|
|
|
for (const auto & f : postProcessor.getPostprocessingList())
|
|
|
|
pFilteredBuffer = f(postProcessor, pFilteredBuffer);
|
2016-05-13 17:29:35 +00:00
|
|
|
|
2016-02-09 17:33:31 +00:00
|
|
|
const f32 viScaleX = _FIXED2FLOAT(_SHIFTR(*REG.VI_X_SCALE, 0, 12), 10);
|
2017-03-09 10:55:16 +00:00
|
|
|
const f32 srcScaleX = pFilteredBuffer->m_scale;
|
2020-07-01 14:59:51 +00:00
|
|
|
const f32 dstScaleX = m_overscan.getDrawingWidth() / (640 * viScaleX);
|
2020-11-14 09:54:28 +00:00
|
|
|
const s32 hx0 = static_cast<s32>(rdpRes.vi_h_start + rdpRes.vi_minhpass);
|
2017-03-01 16:12:39 +00:00
|
|
|
const s32 h0 = (rdpRes.vi_ispal ? 128 : 108);
|
|
|
|
const s32 hEnd = _SHIFTR(*REG.VI_H_START, 0, 10);
|
2020-11-14 09:54:28 +00:00
|
|
|
const s32 hx1 = max(0, h0 + 640 - hEnd + static_cast<s32>(rdpRes.vi_maxhpass));
|
2017-03-01 16:12:39 +00:00
|
|
|
//const s32 hx1 = hx0 + rdpRes.vi_hres;
|
2020-11-14 09:54:28 +00:00
|
|
|
dstX0 = static_cast<s32>((hx0 * viScaleX + f32(XoffsetRight)) * dstScaleX);
|
|
|
|
dstX1 = static_cast<s32>(m_overscan.getDrawingWidth()) - static_cast<s32>(hx1 * viScaleX * dstScaleX);
|
2015-02-09 05:36:20 +00:00
|
|
|
|
2017-03-09 10:55:16 +00:00
|
|
|
const f32 srcScaleY = pFilteredBuffer->m_scale;
|
2016-05-13 17:29:35 +00:00
|
|
|
CachedTexture * pBufferTexture = pFilteredBuffer->m_pTexture;
|
2019-01-20 12:32:39 +00:00
|
|
|
const s32 cutleft = static_cast<s32>(rdpRes.vi_minhpass * viScaleX * srcScaleX);
|
|
|
|
const s32 cutright = static_cast<s32>(rdpRes.vi_maxhpass * viScaleX * srcScaleX);
|
2020-11-14 09:54:28 +00:00
|
|
|
s32 srcCoord[4] = { static_cast<s32>((XoffsetLeft) * srcScaleX) + cutleft,
|
|
|
|
static_cast<s32>(srcY0*srcScaleY),
|
|
|
|
static_cast<s32>((srcWidth + XoffsetLeft - XoffsetRight) * srcScaleX) - cutright,
|
|
|
|
min(static_cast<s32>(srcY1*srcScaleY), static_cast<s32>(pBufferTexture->height)) };
|
2019-05-08 14:04:24 +00:00
|
|
|
if (srcCoord[2] > pBufferTexture->width || srcCoord[3] > pBufferTexture->height) {
|
2015-04-03 05:36:31 +00:00
|
|
|
removeBuffer(pBuffer->m_startAddress);
|
|
|
|
return;
|
|
|
|
}
|
2016-09-09 10:02:31 +00:00
|
|
|
|
2018-05-05 07:45:18 +00:00
|
|
|
const s32 hOffset = m_overscan.getHOffset();
|
|
|
|
const s32 vOffset = m_overscan.getVOffset();
|
2017-03-01 16:12:39 +00:00
|
|
|
s32 dstCoord[4] = { dstX0 + hOffset,
|
2020-11-14 09:54:28 +00:00
|
|
|
vOffset + static_cast<s32>(dstY0*dstScaleY),
|
2018-05-05 07:45:18 +00:00
|
|
|
hOffset + dstX1,
|
2020-11-14 09:54:28 +00:00
|
|
|
vOffset + static_cast<s32>(dstY1*dstScaleY) };
|
2015-04-03 05:36:31 +00:00
|
|
|
|
2017-01-20 11:47:19 +00:00
|
|
|
ObjectHandle readBuffer;
|
2017-01-15 07:57:25 +00:00
|
|
|
|
2016-05-14 12:12:42 +00:00
|
|
|
if (pFilteredBuffer->m_pTexture->frameBufferTexture == CachedTexture::fbMultiSample) {
|
2017-03-09 14:54:37 +00:00
|
|
|
pFilteredBuffer->resolveMultisampledTexture(true);
|
|
|
|
readBuffer = pFilteredBuffer->m_resolveFBO;
|
|
|
|
pBufferTexture = pFilteredBuffer->m_pResolveTexture;
|
2017-01-15 07:57:25 +00:00
|
|
|
} else {
|
2017-02-25 05:59:05 +00:00
|
|
|
readBuffer = pFilteredBuffer->m_FBO;
|
2017-01-15 07:57:25 +00:00
|
|
|
}
|
2016-03-19 14:20:18 +00:00
|
|
|
|
2018-05-05 07:45:18 +00:00
|
|
|
m_overscan.activate();
|
|
|
|
gfxContext.clearColorBuffer(0.0f, 0.0f, 0.0f, 0.0f);
|
2019-06-27 09:21:51 +00:00
|
|
|
#if defined(OS_WINDOWS)
|
|
|
|
gfxContext.clearDepthBuffer();
|
|
|
|
#endif
|
2017-03-09 14:54:37 +00:00
|
|
|
|
2017-01-15 07:57:25 +00:00
|
|
|
GraphicsDrawer::BlitOrCopyRectParams blitParams;
|
|
|
|
blitParams.srcX0 = srcCoord[0];
|
2017-03-10 04:36:03 +00:00
|
|
|
blitParams.srcY0 = srcCoord[1];
|
2017-01-15 07:57:25 +00:00
|
|
|
blitParams.srcX1 = srcCoord[2];
|
2017-03-10 04:36:03 +00:00
|
|
|
blitParams.srcY1 = srcCoord[3];
|
2019-05-08 14:04:24 +00:00
|
|
|
blitParams.srcWidth = pBufferTexture->width;
|
|
|
|
blitParams.srcHeight = pBufferTexture->height;
|
2017-01-15 07:57:25 +00:00
|
|
|
blitParams.dstX0 = dstCoord[0];
|
|
|
|
blitParams.dstY0 = dstCoord[1];
|
|
|
|
blitParams.dstX1 = dstCoord[2];
|
|
|
|
blitParams.dstY1 = dstCoord[3];
|
2018-05-05 07:45:18 +00:00
|
|
|
blitParams.dstWidth = m_overscan.getBufferWidth();
|
|
|
|
blitParams.dstHeight = m_overscan.getBufferHeight();
|
2017-01-20 11:47:19 +00:00
|
|
|
blitParams.mask = blitMask::COLOR_BUFFER;
|
2017-01-15 07:57:25 +00:00
|
|
|
blitParams.tex[0] = pBufferTexture;
|
2020-07-28 07:24:01 +00:00
|
|
|
const bool downscale = blitParams.srcWidth >= blitParams.dstWidth || blitParams.srcHeight >= blitParams.dstHeight;
|
|
|
|
blitParams.filter = downscale || config.generalEmulation.enableHybridFilter > 0 ?
|
|
|
|
textureParameters::FILTER_LINEAR :
|
|
|
|
textureParameters::FILTER_NEAREST; //upscale; hybridFilter disabled
|
2019-06-27 09:21:51 +00:00
|
|
|
if (config.frameBufferEmulation.copyDepthToMainDepthBuffer != 0) {
|
|
|
|
blitParams.tex[1] = pBuffer->m_pDepthTexture;
|
2020-07-01 15:01:14 +00:00
|
|
|
blitParams.combiner = downscale ? CombinerInfo::get().getTexrectColorAndDepthDownscaleCopyProgram() :
|
2020-05-05 10:20:25 +00:00
|
|
|
CombinerInfo::get().getTexrectColorAndDepthUpscaleCopyProgram();
|
2019-09-29 09:13:03 +00:00
|
|
|
}
|
|
|
|
if (blitParams.combiner == nullptr) {
|
|
|
|
// copyDepthToMainDepthBuffer not set or not supported
|
2020-05-05 10:20:25 +00:00
|
|
|
blitParams.combiner = downscale ? CombinerInfo::get().getTexrectDownscaleCopyProgram() :
|
|
|
|
CombinerInfo::get().getTexrectUpscaleCopyProgram();
|
2019-06-27 09:21:51 +00:00
|
|
|
}
|
2017-01-15 07:57:25 +00:00
|
|
|
blitParams.readBuffer = readBuffer;
|
2018-05-05 07:45:18 +00:00
|
|
|
blitParams.invertY = config.frameBufferEmulation.enableOverscan == 0;
|
2017-01-15 07:57:25 +00:00
|
|
|
|
2017-03-10 04:36:03 +00:00
|
|
|
drawer.copyTexturedRect(blitParams);
|
2014-09-26 12:19:23 +00:00
|
|
|
|
2017-03-13 14:45:21 +00:00
|
|
|
if (pNextBuffer != nullptr) {
|
|
|
|
pNextBuffer->m_isMainBuffer = true;
|
2018-09-09 09:34:00 +00:00
|
|
|
pFilteredBuffer = pNextBuffer;
|
|
|
|
for (const auto & f : postProcessor.getPostprocessingList())
|
|
|
|
pFilteredBuffer = f(postProcessor, pFilteredBuffer);
|
2017-03-13 14:45:21 +00:00
|
|
|
srcY1 = srcPartHeight;
|
|
|
|
dstY0 = dstY1;
|
|
|
|
dstY1 = dstY0 + dstPartHeight;
|
|
|
|
if (pFilteredBuffer->m_pTexture->frameBufferTexture == CachedTexture::fbMultiSample) {
|
|
|
|
pFilteredBuffer->resolveMultisampledTexture();
|
|
|
|
readBuffer = pFilteredBuffer->m_resolveFBO;
|
|
|
|
pBufferTexture = pFilteredBuffer->m_pResolveTexture;
|
2019-06-27 09:21:51 +00:00
|
|
|
} else {
|
2017-03-13 14:45:21 +00:00
|
|
|
readBuffer = pFilteredBuffer->m_FBO;
|
|
|
|
pBufferTexture = pFilteredBuffer->m_pTexture;
|
2013-09-29 15:02:50 +00:00
|
|
|
}
|
2017-03-13 14:45:21 +00:00
|
|
|
|
|
|
|
blitParams.srcY0 = 0;
|
2020-11-14 09:54:28 +00:00
|
|
|
blitParams.srcY1 = min(static_cast<s32>(srcY1*srcScaleY), static_cast<s32>(pFilteredBuffer->m_pTexture->height));
|
2019-05-08 14:04:24 +00:00
|
|
|
blitParams.srcWidth = pBufferTexture->width;
|
|
|
|
blitParams.srcHeight = pBufferTexture->height;
|
2020-11-14 09:54:28 +00:00
|
|
|
blitParams.dstY0 = vOffset + static_cast<s32>(dstY0*dstScaleY);
|
|
|
|
blitParams.dstY1 = vOffset + static_cast<s32>(dstY1*dstScaleY);
|
2018-05-05 07:45:18 +00:00
|
|
|
blitParams.dstWidth = m_overscan.getBufferWidth();
|
|
|
|
blitParams.dstHeight = m_overscan.getBufferHeight();
|
2017-03-13 14:45:21 +00:00
|
|
|
blitParams.tex[0] = pBufferTexture;
|
2019-06-27 09:21:51 +00:00
|
|
|
blitParams.tex[1] = pNextBuffer->m_pDepthTexture;
|
|
|
|
blitParams.mask = blitMask::COLOR_BUFFER;
|
2017-03-13 14:45:21 +00:00
|
|
|
blitParams.readBuffer = readBuffer;
|
|
|
|
|
|
|
|
drawer.copyTexturedRect(blitParams);
|
2013-09-29 15:02:50 +00:00
|
|
|
}
|
2014-09-26 12:19:23 +00:00
|
|
|
|
2018-04-03 15:08:06 +00:00
|
|
|
gfxContext.bindFramebuffer(bufferTarget::READ_FRAMEBUFFER, ObjectHandle::defaultFramebuffer);
|
2018-05-05 07:45:18 +00:00
|
|
|
m_overscan.draw(vFullHeight, rdpRes.vi_ispal);
|
2017-01-20 11:47:19 +00:00
|
|
|
|
2017-01-15 07:57:25 +00:00
|
|
|
wnd.swapBuffers();
|
2016-07-09 19:07:03 +00:00
|
|
|
if (m_pCurrent != nullptr) {
|
2017-02-25 05:59:05 +00:00
|
|
|
gfxContext.bindFramebuffer(bufferTarget::DRAW_FRAMEBUFFER, m_pCurrent->m_FBO);
|
2016-07-09 19:07:03 +00:00
|
|
|
}
|
2018-05-01 12:21:30 +00:00
|
|
|
if (config.frameBufferEmulation.forceDepthBufferClear != 0) {
|
2018-09-27 11:31:41 +00:00
|
|
|
drawer.clearDepthBuffer();
|
2018-05-01 12:21:30 +00:00
|
|
|
}
|
2017-08-05 17:16:34 +00:00
|
|
|
|
|
|
|
const s32 X = hOffset;
|
2020-11-14 09:54:28 +00:00
|
|
|
const s32 Y = static_cast<s32>(wnd.getHeightOffset());
|
|
|
|
const s32 W = static_cast<s32>(wnd.getWidth());
|
|
|
|
const s32 H = static_cast<s32>(wnd.getHeight());
|
2017-08-05 17:16:34 +00:00
|
|
|
|
|
|
|
gfxContext.setScissor(X, Y, W, H);
|
2014-06-09 04:26:43 +00:00
|
|
|
gDP.changed |= CHANGED_SCISSOR;
|
2013-06-01 13:13:04 +00:00
|
|
|
}
|
2015-11-23 12:36:03 +00:00
|
|
|
|
2016-09-27 14:43:28 +00:00
|
|
|
void FrameBufferList::fillRDRAM(s32 ulx, s32 uly, s32 lrx, s32 lry)
|
|
|
|
{
|
|
|
|
if (m_pCurrent == nullptr)
|
|
|
|
return;
|
|
|
|
|
2018-10-17 08:51:00 +00:00
|
|
|
if (config.frameBufferEmulation.copyFromRDRAM !=0 && !m_pCurrent->m_isDepthBuffer)
|
|
|
|
// Do not write to RDRAM color buffer if copyFromRDRAM enabled.
|
|
|
|
return;
|
|
|
|
|
2020-11-14 09:54:28 +00:00
|
|
|
ulx = static_cast<s32>(min(max(static_cast<f32>(ulx), gDP.scissor.ulx), gDP.scissor.lrx));
|
|
|
|
lrx = static_cast<s32>(min(max(static_cast<f32>(lrx), gDP.scissor.ulx), gDP.scissor.lrx));
|
|
|
|
uly = static_cast<s32>(min(max(static_cast<f32>(uly), gDP.scissor.uly), gDP.scissor.lry));
|
|
|
|
lry = static_cast<s32>(min(max(static_cast<f32>(lry), gDP.scissor.uly), gDP.scissor.lry));
|
2016-09-27 14:43:28 +00:00
|
|
|
|
|
|
|
const u32 stride = gDP.colorImage.width << gDP.colorImage.size >> 1;
|
2020-11-14 09:54:28 +00:00
|
|
|
const u32 lowerBound = gDP.colorImage.address + static_cast<u32>(lry)*stride;
|
2016-09-27 14:43:28 +00:00
|
|
|
if (lowerBound > RDRAMSize)
|
|
|
|
lry -= (lowerBound - RDRAMSize) / stride;
|
|
|
|
u32 ci_width_in_dwords = gDP.colorImage.width >> (3 - gDP.colorImage.size);
|
|
|
|
ulx >>= (3 - gDP.colorImage.size);
|
|
|
|
lrx >>= (3 - gDP.colorImage.size);
|
2020-11-14 09:54:28 +00:00
|
|
|
u32 * dst = reinterpret_cast<u32*>(RDRAM + gDP.colorImage.address);
|
|
|
|
dst += static_cast<u32>(uly) * ci_width_in_dwords;
|
2019-03-15 10:04:33 +00:00
|
|
|
if (!isMemoryWritable(dst, lowerBound - gDP.colorImage.address))
|
|
|
|
return;
|
2017-11-15 07:42:04 +00:00
|
|
|
for (s32 y = uly; y < lry; ++y) {
|
|
|
|
for (s32 x = ulx; x < lrx; ++x) {
|
2016-09-27 14:43:28 +00:00
|
|
|
dst[x] = gDP.fillColor.color;
|
|
|
|
}
|
|
|
|
dst += ci_width_in_dwords;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_pCurrent->setBufferClearParams(gDP.fillColor.color, ulx, uly, lrx, lry);
|
|
|
|
}
|
|
|
|
|
2017-09-29 15:24:30 +00:00
|
|
|
void FrameBuffer_ActivateBufferTexture(u32 t, u32 _frameBufferAddress)
|
2013-09-05 16:24:34 +00:00
|
|
|
{
|
2017-09-29 15:24:30 +00:00
|
|
|
FrameBuffer * pBuffer = frameBufferList().getBuffer(_frameBufferAddress);
|
2016-06-10 06:06:06 +00:00
|
|
|
if (pBuffer == nullptr)
|
2014-10-18 11:56:19 +00:00
|
|
|
return;
|
2015-02-09 12:27:39 +00:00
|
|
|
|
2016-05-08 09:58:19 +00:00
|
|
|
CachedTexture *pTexture = pBuffer->getTexture(t);
|
2016-06-10 06:06:06 +00:00
|
|
|
if (pTexture == nullptr)
|
2016-05-08 09:58:19 +00:00
|
|
|
return;
|
2013-09-05 16:24:34 +00:00
|
|
|
|
2014-10-03 16:27:05 +00:00
|
|
|
// frameBufferList().renderBuffer(pBuffer->m_startAddress);
|
2015-02-09 12:27:39 +00:00
|
|
|
textureCache().activateTexture(t, pTexture);
|
2014-04-27 12:26:12 +00:00
|
|
|
gDP.changed |= CHANGED_FB_TEXTURE;
|
2013-09-05 16:24:34 +00:00
|
|
|
}
|
|
|
|
|
2017-09-29 15:24:30 +00:00
|
|
|
void FrameBuffer_ActivateBufferTextureBG(u32 t, u32 _frameBufferAddress)
|
2013-09-05 16:24:34 +00:00
|
|
|
{
|
2017-09-29 15:24:30 +00:00
|
|
|
FrameBuffer * pBuffer = frameBufferList().getBuffer(_frameBufferAddress);
|
2016-06-10 06:06:06 +00:00
|
|
|
if (pBuffer == nullptr)
|
2015-02-09 12:27:39 +00:00
|
|
|
return;
|
2013-09-05 16:24:34 +00:00
|
|
|
|
2020-11-14 09:54:28 +00:00
|
|
|
CachedTexture *pTexture = pBuffer->getTextureBG();
|
2016-06-10 06:06:06 +00:00
|
|
|
if (pTexture == nullptr)
|
2016-05-08 09:58:19 +00:00
|
|
|
return;
|
2013-09-05 16:24:34 +00:00
|
|
|
|
2016-05-08 09:58:19 +00:00
|
|
|
// frameBufferList().renderBuffer(pBuffer->m_startAddress);
|
2015-02-09 12:27:39 +00:00
|
|
|
textureCache().activateTexture(t, pTexture);
|
2014-04-27 12:26:12 +00:00
|
|
|
gDP.changed |= CHANGED_FB_TEXTURE;
|
2013-09-05 16:24:34 +00:00
|
|
|
}
|
2013-08-10 11:12:17 +00:00
|
|
|
|
2015-10-31 03:11:33 +00:00
|
|
|
void FrameBuffer_CopyToRDRAM(u32 _address, bool _sync)
|
2013-06-09 11:55:21 +00:00
|
|
|
{
|
2022-01-16 17:27:17 +00:00
|
|
|
#ifndef NATIVE
|
2016-05-28 15:37:20 +00:00
|
|
|
ColorBufferToRDRAM::get().copyToRDRAM(_address, _sync);
|
2022-01-16 17:27:17 +00:00
|
|
|
#endif
|
2013-06-09 11:55:21 +00:00
|
|
|
}
|
2013-09-07 16:31:04 +00:00
|
|
|
|
2015-11-23 13:03:33 +00:00
|
|
|
void FrameBuffer_CopyChunkToRDRAM(u32 _address)
|
|
|
|
{
|
2016-05-28 15:37:20 +00:00
|
|
|
ColorBufferToRDRAM::get().copyChunkToRDRAM(_address);
|
2015-11-23 13:03:33 +00:00
|
|
|
}
|
|
|
|
|
2022-01-16 17:27:17 +00:00
|
|
|
bool FrameBuffer_CopyDepthBuffer( word address )
|
2015-11-23 13:03:33 +00:00
|
|
|
{
|
2017-03-29 10:01:59 +00:00
|
|
|
FrameBufferList & fblist = frameBufferList();
|
|
|
|
FrameBuffer * pCopyBuffer = fblist.getCopyBuffer();
|
2016-06-10 06:06:06 +00:00
|
|
|
if (pCopyBuffer != nullptr) {
|
2015-09-27 04:10:05 +00:00
|
|
|
// This code is mainly to emulate Zelda MM camera.
|
2016-05-28 15:37:20 +00:00
|
|
|
ColorBufferToRDRAM::get().copyToRDRAM(pCopyBuffer->m_startAddress, true);
|
2015-09-27 04:10:05 +00:00
|
|
|
pCopyBuffer->m_RdramCopy.resize(0); // To disable validity check by RDRAM content. CPU may change content of the buffer for some unknown reason.
|
2017-03-29 10:01:59 +00:00
|
|
|
fblist.setCopyBuffer(nullptr);
|
2015-09-27 04:10:05 +00:00
|
|
|
return true;
|
2017-03-29 10:01:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (DepthBufferToRDRAM::get().copyToRDRAM(address)) {
|
|
|
|
fblist.depthBufferCopyRdram();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2015-11-23 13:03:33 +00:00
|
|
|
}
|
|
|
|
|
2022-01-16 17:27:17 +00:00
|
|
|
bool FrameBuffer_CopyDepthBufferChunk(word address)
|
2015-11-23 13:03:33 +00:00
|
|
|
{
|
2016-05-28 15:37:20 +00:00
|
|
|
return DepthBufferToRDRAM::get().copyChunkToRDRAM(address);
|
2013-11-12 08:41:19 +00:00
|
|
|
}
|
|
|
|
|
2022-01-16 17:27:17 +00:00
|
|
|
void FrameBuffer_CopyFromRDRAM(word _address, bool _bCFB)
|
2013-09-07 16:31:04 +00:00
|
|
|
{
|
2016-05-28 15:37:20 +00:00
|
|
|
RDRAMtoColorBuffer::get().copyFromRDRAM(_address, _bCFB);
|
2013-09-07 16:31:04 +00:00
|
|
|
}
|
2016-02-02 18:52:29 +00:00
|
|
|
|
2022-01-16 17:27:17 +00:00
|
|
|
void FrameBuffer_AddAddress(word address, u32 _size)
|
2016-02-02 18:52:29 +00:00
|
|
|
{
|
2016-05-28 15:37:20 +00:00
|
|
|
RDRAMtoColorBuffer::get().addAddress(address, _size);
|
2016-02-02 18:52:29 +00:00
|
|
|
}
|
2017-03-05 07:12:49 +00:00
|
|
|
|
|
|
|
u32 cutHeight(u32 _address, u32 _height, u32 _stride)
|
|
|
|
{
|
|
|
|
return _cutHeight(_address, _height, _stride);
|
|
|
|
}
|
|
|
|
|
|
|
|
void calcCoordsScales(const FrameBuffer * _pBuffer, f32 & _scaleX, f32 & _scaleY)
|
|
|
|
{
|
|
|
|
const u32 bufferWidth = _pBuffer != nullptr ? _pBuffer->m_width : VI.width;
|
2020-11-14 09:54:28 +00:00
|
|
|
const u32 bufferHeight = VI_GetMaxBufferHeight(static_cast<u16>(bufferWidth));
|
2017-03-05 07:12:49 +00:00
|
|
|
_scaleX = 1.0f / f32(bufferWidth);
|
|
|
|
_scaleY = 1.0f / f32(bufferHeight);
|
|
|
|
}
|