mirror of
https://github.com/blawar/GLideN64.git
synced 2024-07-04 10:03:36 +00:00
291 lines
9.0 KiB
C++
291 lines
9.0 KiB
C++
#include <assert.h>
|
|
#include <algorithm>
|
|
|
|
#include "ColorBufferToRDRAM.h"
|
|
#include "WriteToRDRAM.h"
|
|
|
|
#include <FrameBuffer.h>
|
|
#include <FrameBufferInfo.h>
|
|
#include <Config.h>
|
|
#include <N64.h>
|
|
#include <VI.h>
|
|
#include "Log.h"
|
|
#include "MemoryStatus.h"
|
|
|
|
#include <Graphics/Context.h>
|
|
#include <Graphics/Parameters.h>
|
|
#include <Graphics/ColorBufferReader.h>
|
|
#include <DisplayWindow.h>
|
|
#include "BlueNoiseTexture.h"
|
|
|
|
using namespace graphics;
|
|
|
|
ColorBufferToRDRAM::ColorBufferToRDRAM()
|
|
: m_pCurFrameBuffer(nullptr)
|
|
, m_frameCount(-1)
|
|
, m_startAddress(-1)
|
|
{
|
|
}
|
|
|
|
u32 ColorBufferToRDRAM::m_blueNoiseIdx = 0;
|
|
|
|
ColorBufferToRDRAM::~ColorBufferToRDRAM()
|
|
{
|
|
}
|
|
|
|
void ColorBufferToRDRAM::init()
|
|
{
|
|
}
|
|
|
|
void ColorBufferToRDRAM::destroy() {
|
|
|
|
}
|
|
|
|
bool ColorBufferToRDRAM::_prepareCopy(u32& _startAddress)
|
|
{
|
|
if (VI.width == 0 || frameBufferList().getCurrent() == nullptr)
|
|
return false;
|
|
|
|
FrameBuffer * pBuffer = frameBufferList().findBuffer(_startAddress);
|
|
if (pBuffer == nullptr || pBuffer->m_isOBScreen)
|
|
return false;
|
|
|
|
DisplayWindow & wnd = dwnd();
|
|
const u32 curFrame = wnd.getBuffersSwapCount();
|
|
|
|
_startAddress &= ~0xfff;
|
|
if (_startAddress < pBuffer->m_startAddress)
|
|
_startAddress = pBuffer->m_startAddress;
|
|
|
|
if (m_frameCount == curFrame && pBuffer == m_pCurFrameBuffer && m_startAddress != _startAddress)
|
|
return true;
|
|
|
|
const u32 numPixels = pBuffer->m_width * pBuffer->m_height;
|
|
if (numPixels == 0)
|
|
return false;
|
|
|
|
const u32 stride = pBuffer->m_width << pBuffer->m_size >> 1;
|
|
const u32 bufferHeight = cutHeight(_startAddress, pBuffer->m_height, stride);
|
|
if (bufferHeight == 0)
|
|
return false;
|
|
|
|
CachedTexture* colorBufferTexture = pBuffer->getColorFbTexture();
|
|
|
|
m_pCurFrameBuffer = pBuffer;
|
|
|
|
if ((config.generalEmulation.hacks & hack_subscreen) != 0 && m_pCurFrameBuffer->m_width == VI.width) {
|
|
copyWhiteToRDRAM(m_pCurFrameBuffer);
|
|
return false;
|
|
}
|
|
|
|
ObjectHandle readBuffer;
|
|
|
|
if (config.video.multisampling != 0) {
|
|
m_pCurFrameBuffer->resolveMultisampledTexture();
|
|
readBuffer = m_pCurFrameBuffer->m_resolveFBO;
|
|
} else {
|
|
readBuffer = m_pCurFrameBuffer->m_FBO;
|
|
}
|
|
|
|
u32 x0 = 0;
|
|
u32 width;
|
|
if (config.frameBufferEmulation.nativeResFactor == 0) {
|
|
const u32 screenWidth = wnd.getWidth();
|
|
width = screenWidth;
|
|
if (wnd.isAdjustScreen()) {
|
|
width = static_cast<u32>(screenWidth*wnd.getAdjustScale());
|
|
x0 = (screenWidth - width) / 2;
|
|
}
|
|
} else {
|
|
width = m_pCurFrameBuffer->m_pTexture->width;
|
|
}
|
|
u32 height = (u32)(bufferHeight * m_pCurFrameBuffer->m_scale);
|
|
|
|
CachedTexture * pInputTexture = m_pCurFrameBuffer->m_pTexture;
|
|
GraphicsDrawer::BlitOrCopyRectParams blitParams;
|
|
blitParams.srcX0 = x0;
|
|
blitParams.srcY0 = 0;
|
|
blitParams.srcX1 = x0 + width;
|
|
blitParams.srcY1 = height;
|
|
blitParams.srcWidth = pInputTexture->width;
|
|
blitParams.srcHeight = pInputTexture->height;
|
|
blitParams.dstX0 = 0;
|
|
blitParams.dstY0 = 0;
|
|
blitParams.dstX1 = m_pCurFrameBuffer->m_width;
|
|
blitParams.dstY1 = bufferHeight;
|
|
blitParams.dstWidth = colorBufferTexture->width;
|
|
blitParams.dstHeight = colorBufferTexture->height;
|
|
blitParams.filter = m_pCurFrameBuffer->m_scale == 1.0f ? textureParameters::FILTER_NEAREST : textureParameters::FILTER_LINEAR;
|
|
blitParams.tex[0] = pInputTexture;
|
|
blitParams.combiner = CombinerInfo::get().getTexrectDownscaleCopyProgram();
|
|
blitParams.readBuffer = readBuffer;
|
|
blitParams.drawBuffer = pBuffer->getColorFbFbo();
|
|
blitParams.mask = blitMask::COLOR_BUFFER;
|
|
wnd.getDrawer().blitOrCopyTexturedRect(blitParams);
|
|
|
|
gfxContext.bindFramebuffer(bufferTarget::READ_FRAMEBUFFER, pBuffer->getColorFbFbo());
|
|
|
|
m_frameCount = curFrame;
|
|
m_startAddress = _startAddress;
|
|
return true;
|
|
}
|
|
|
|
u8 ColorBufferToRDRAM::_RGBAtoR8(u8 _c, u32 x, u32 y) {
|
|
return _c;
|
|
}
|
|
|
|
u16 ColorBufferToRDRAM::_RGBAtoRGBA16(u32 _c, u32 x, u32 y) {
|
|
// Precalculated 4x4 bayer matrix values for 5Bit
|
|
static const s32 thresholdMapBayer[4][4] = {
|
|
{ -4, 2, -3, 4 },
|
|
{ 0, -2, 2, -1 },
|
|
{ -3, 3, -4, 3 },
|
|
{ 1, -1, 1, -2 }
|
|
};
|
|
|
|
// Precalculated 4x4 magic square matrix values for 5Bit
|
|
static const s32 thresholdMapMagicSquare[4][4] = {
|
|
{ -4, 2, 2, -1 },
|
|
{ 3, -2, -3, 1 },
|
|
{ -3, 0, 4, -2 },
|
|
{ 3, -1, -4, 1 }
|
|
};
|
|
|
|
union RGBA c;
|
|
c.raw = _c;
|
|
|
|
if (config.generalEmulation.enableDitheringPattern == 0 || config.frameBufferEmulation.nativeResFactor != 1) {
|
|
// Apply color dithering
|
|
switch (config.generalEmulation.rdramImageDitheringMode) {
|
|
case Config::BufferDitheringMode::bdmBayer:
|
|
case Config::BufferDitheringMode::bdmMagicSquare:
|
|
{
|
|
s32 threshold = config.generalEmulation.rdramImageDitheringMode == Config::BufferDitheringMode::bdmBayer ?
|
|
thresholdMapBayer[x & 3][y & 3] :
|
|
thresholdMapMagicSquare[x & 3][y & 3];
|
|
c.r = (u8)std::max(std::min((s32)c.r + threshold, 255), 0);
|
|
c.g = (u8)std::max(std::min((s32)c.g + threshold, 255), 0);
|
|
c.b = (u8)std::max(std::min((s32)c.b + threshold, 255), 0);
|
|
}
|
|
break;
|
|
case Config::BufferDitheringMode::bdmBlueNoise:
|
|
{
|
|
const BlueNoiseItem& threshold = blueNoiseTex[m_blueNoiseIdx & 7][x & 63][y & 63];
|
|
c.r = (u8)std::max(std::min((s32)c.r + threshold.r, 255), 0);
|
|
c.g = (u8)std::max(std::min((s32)c.g + threshold.g, 255), 0);
|
|
c.b = (u8)std::max(std::min((s32)c.b + threshold.b, 255), 0);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ((c.r >> 3) << 11) | ((c.g >> 3) << 6) | ((c.b >> 3) << 1) | (c.a == 0 ? 0 : 1);
|
|
}
|
|
|
|
u32 ColorBufferToRDRAM::_RGBAtoRGBA32(u32 _c, u32 x, u32 y) {
|
|
RGBA c;
|
|
c.raw = _c;
|
|
return (c.r << 24) | (c.g << 16) | (c.b << 8) | c.a;
|
|
}
|
|
|
|
void ColorBufferToRDRAM::_copy(u32 _startAddress, u32 _endAddress, bool _sync)
|
|
{
|
|
const u32 stride = m_pCurFrameBuffer->m_width << m_pCurFrameBuffer->m_size >> 1;
|
|
const u32 max_height = std::min((u32)VI_GetMaxBufferHeight(m_pCurFrameBuffer->m_width), cutHeight(_startAddress, m_pCurFrameBuffer->m_height, stride));
|
|
|
|
u32 numPixels = (_endAddress - _startAddress) >> (m_pCurFrameBuffer->m_size - 1);
|
|
if (numPixels / m_pCurFrameBuffer->m_width > max_height) {
|
|
_endAddress = _startAddress + (max_height * stride);
|
|
numPixels = (_endAddress - _startAddress) >> (m_pCurFrameBuffer->m_size - 1);
|
|
}
|
|
|
|
const u32 width = m_pCurFrameBuffer->m_width;
|
|
const s32 x0 = 0;
|
|
const s32 y0 = (_startAddress - m_pCurFrameBuffer->m_startAddress) / stride;
|
|
const u32 y1 = (_endAddress - m_pCurFrameBuffer->m_startAddress) / stride;
|
|
const u32 height = std::min(max_height, 1u + y1 - y0);
|
|
|
|
const u8* pPixels = m_pCurFrameBuffer->readPixels(x0, y0, width, height, m_pCurFrameBuffer->m_size, _sync);
|
|
frameBufferList().setCurrentDrawBuffer();
|
|
if (pPixels == nullptr)
|
|
return;
|
|
|
|
if (m_pCurFrameBuffer->m_size == G_IM_SIZ_32b) {
|
|
u32 *ptr_src = (u32*)pPixels;
|
|
u32 *ptr_dst = (u32*)(RDRAM + _startAddress);
|
|
writeToRdram<u32, u32>(ptr_src, ptr_dst, &ColorBufferToRDRAM::_RGBAtoRGBA32, valueTester<u32, 0>, 0, width, height, numPixels, _startAddress, m_pCurFrameBuffer->m_startAddress, m_pCurFrameBuffer->m_size);
|
|
} else if (m_pCurFrameBuffer->m_size == G_IM_SIZ_16b) {
|
|
u32 *ptr_src = (u32*)pPixels;
|
|
u16 *ptr_dst = (u16*)(RDRAM + _startAddress);
|
|
m_blueNoiseIdx++;
|
|
writeToRdram<u32, u16>(ptr_src, ptr_dst, &ColorBufferToRDRAM::_RGBAtoRGBA16, valueTester<u32, 0>, 1, width, height, numPixels, _startAddress, m_pCurFrameBuffer->m_startAddress, m_pCurFrameBuffer->m_size);
|
|
} else if (m_pCurFrameBuffer->m_size == G_IM_SIZ_8b) {
|
|
u8 *ptr_src = (u8*)pPixels;
|
|
u8 *ptr_dst = RDRAM + _startAddress;
|
|
writeToRdram<u8, u8>(ptr_src, ptr_dst, &ColorBufferToRDRAM::_RGBAtoR8, dummyTester<u8>, 3, width, height, numPixels, _startAddress, m_pCurFrameBuffer->m_startAddress, m_pCurFrameBuffer->m_size);
|
|
}
|
|
|
|
m_pCurFrameBuffer->m_copiedToRdram = true;
|
|
m_pCurFrameBuffer->copyRdram();
|
|
m_pCurFrameBuffer->m_cleared = false;
|
|
|
|
m_pCurFrameBuffer->cleanUp();
|
|
|
|
gDP.changed |= CHANGED_SCISSOR;
|
|
}
|
|
|
|
void ColorBufferToRDRAM::copyToRDRAM(u32 _address, bool _sync)
|
|
{
|
|
if (!isMemoryWritable(RDRAM + _address, gDP.colorImage.width << gDP.colorImage.size >> 1))
|
|
return;
|
|
if (!_prepareCopy(_address))
|
|
return;
|
|
if (config.frameBufferEmulation.copyToRDRAM == Config::CopyToRDRAM::ctDisable)
|
|
return;
|
|
|
|
const u32 numBytes = (m_pCurFrameBuffer->m_width*m_pCurFrameBuffer->m_height) << m_pCurFrameBuffer->m_size >> 1;
|
|
_copy(m_pCurFrameBuffer->m_startAddress, m_pCurFrameBuffer->m_startAddress + numBytes, _sync);
|
|
}
|
|
|
|
void ColorBufferToRDRAM::copyChunkToRDRAM(u32 _startAddress)
|
|
{
|
|
const u32 endAddress = (_startAddress & ~0xfff) + 0x1000;
|
|
|
|
if (!isMemoryWritable(RDRAM + _startAddress, endAddress - _startAddress))
|
|
return;
|
|
if (!_prepareCopy(_startAddress))
|
|
return;
|
|
_copy(_startAddress, endAddress, true);
|
|
}
|
|
|
|
|
|
ColorBufferToRDRAM & ColorBufferToRDRAM::get()
|
|
{
|
|
static ColorBufferToRDRAM cbCopy;
|
|
return cbCopy;
|
|
}
|
|
|
|
void copyWhiteToRDRAM(FrameBuffer * _pBuffer)
|
|
{
|
|
if (_pBuffer->m_size == G_IM_SIZ_32b) {
|
|
u32 *ptr_dst = (u32*)(RDRAM + _pBuffer->m_startAddress);
|
|
|
|
for (u32 y = 0; y < VI.height; ++y) {
|
|
for (u32 x = 0; x < VI.width; ++x)
|
|
ptr_dst[x + y*VI.width] = 0xFFFFFFFF;
|
|
}
|
|
} else {
|
|
u16 *ptr_dst = (u16*)(RDRAM + _pBuffer->m_startAddress);
|
|
|
|
for (u32 y = 0; y < VI.height; ++y) {
|
|
for (u32 x = 0; x < VI.width; ++x) {
|
|
ptr_dst[(x + y*VI.width) ^ 1] = 0xFFFF;
|
|
}
|
|
}
|
|
}
|
|
_pBuffer->m_copiedToRdram = true;
|
|
_pBuffer->copyRdram();
|
|
|
|
_pBuffer->m_cleared = false;
|
|
}
|