GPU: Implement the DISPCNT register's ForceBlank bit by clearing the line to white if the ForceBlank bit is set. (Fixes #775.)

This commit is contained in:
rogerman 2024-03-02 15:18:51 -08:00
parent 0a6eca6dce
commit bae67e2d0c
2 changed files with 70 additions and 41 deletions

View File

@ -924,6 +924,11 @@ const GPU_IOREG& GPUEngineBase::GetIORegisterMap() const
return *this->_IORegisterMap; return *this->_IORegisterMap;
} }
bool GPUEngineBase::IsForceBlankSet() const
{
return (this->_IORegisterMap->DISPCNT.ForceBlank != 0);
}
bool GPUEngineBase::IsMasterBrightMaxOrMin() const bool GPUEngineBase::IsMasterBrightMaxOrMin() const
{ {
return this->_currentRenderState.masterBrightnessIsMaxOrMin; return this->_currentRenderState.masterBrightnessIsMaxOrMin;
@ -2940,11 +2945,18 @@ void GPUEngineBase::RenderLayerBG(const GPULayerID layerID, u16 *dstColorBuffer)
} }
} }
void GPUEngineBase::_RenderLineBlank(const size_t l)
{
// Native rendering only.
// Just clear the line using white pixels.
memset_u16_fast<GPU_FRAMEBUFFER_NATIVE_WIDTH>(this->_targetDisplay->GetNativeBuffer16() + (l * GPU_FRAMEBUFFER_NATIVE_WIDTH), 0xFFFF);
}
void GPUEngineBase::_HandleDisplayModeOff(const size_t l) void GPUEngineBase::_HandleDisplayModeOff(const size_t l)
{ {
// Native rendering only. // Native rendering only.
// In this display mode, the display is cleared to white. // In this display mode, the line is cleared to white.
memset_u16_fast<GPU_FRAMEBUFFER_NATIVE_WIDTH>(this->_targetDisplay->GetNativeBuffer16() + (l * GPU_FRAMEBUFFER_NATIVE_WIDTH), 0xFFFF); this->_RenderLineBlank(l);
} }
void GPUEngineBase::_HandleDisplayModeNormal(const size_t l) void GPUEngineBase::_HandleDisplayModeNormal(const size_t l)
@ -3536,23 +3548,30 @@ void GPUEngineA::RenderLine(const size_t l)
} }
// Fill the display output // Fill the display output
switch (compInfo.renderState.displayOutputMode) if ( this->IsForceBlankSet() )
{ {
case GPUDisplayMode_Off: // Display Off (Display white) this->_RenderLineBlank(l);
this->_HandleDisplayModeOff(l); }
break; else
{
case GPUDisplayMode_Normal: // Display BG and OBJ layers switch (compInfo.renderState.displayOutputMode)
this->_HandleDisplayModeNormal(l); {
break; case GPUDisplayMode_Off: // Display Off (clear line to white)
this->_HandleDisplayModeOff(l);
case GPUDisplayMode_VRAM: // Display VRAM framebuffer break;
this->_HandleDisplayModeVRAM<OUTPUTFORMAT>(compInfo.line);
break; case GPUDisplayMode_Normal: // Display BG and OBJ layers
this->_HandleDisplayModeNormal(l);
case GPUDisplayMode_MainMemory: // Display Memory FIFO break;
this->_HandleDisplayModeMainMemory(compInfo.line);
break; case GPUDisplayMode_VRAM: // Display VRAM framebuffer
this->_HandleDisplayModeVRAM<OUTPUTFORMAT>(compInfo.line);
break;
case GPUDisplayMode_MainMemory: // Display Memory FIFO
this->_HandleDisplayModeMainMemory(compInfo.line);
break;
}
} }
//capture after displaying so that we can safely display vram before overwriting it here //capture after displaying so that we can safely display vram before overwriting it here
@ -4533,29 +4552,36 @@ void GPUEngineB::RenderLine(const size_t l)
{ {
GPUEngineCompositorInfo &compInfo = this->_currentCompositorInfo[l]; GPUEngineCompositorInfo &compInfo = this->_currentCompositorInfo[l];
switch (compInfo.renderState.displayOutputMode) if ( this->IsForceBlankSet() )
{ {
case GPUDisplayMode_Off: // Display Off(Display white) this->_RenderLineBlank(l);
this->_HandleDisplayModeOff(l); }
break; else
{
case GPUDisplayMode_Normal: // Display BG and OBJ layers switch (compInfo.renderState.displayOutputMode)
{ {
if (compInfo.renderState.isAnyWindowEnabled) case GPUDisplayMode_Off: // Display Off (clear line to white)
{ this->_HandleDisplayModeOff(l);
this->_RenderLine_Layers<OUTPUTFORMAT, true>(compInfo); break;
}
else
{
this->_RenderLine_Layers<OUTPUTFORMAT, false>(compInfo);
}
this->_HandleDisplayModeNormal(l); case GPUDisplayMode_Normal: // Display BG and OBJ layers
break; {
if (compInfo.renderState.isAnyWindowEnabled)
{
this->_RenderLine_Layers<OUTPUTFORMAT, true>(compInfo);
}
else
{
this->_RenderLine_Layers<OUTPUTFORMAT, false>(compInfo);
}
this->_HandleDisplayModeNormal(l);
break;
}
default:
break;
} }
default:
break;
} }
if (compInfo.line.indexNative >= 191) if (compInfo.line.indexNative >= 191)
@ -5454,7 +5480,7 @@ void GPUSubsystem::RenderLine(const size_t l)
this->_engineSub->UpdateRenderStates(l); this->_engineSub->UpdateRenderStates(l);
} }
if ( (isFramebufferRenderNeeded[GPUEngineID_Main] || isDisplayCaptureNeeded) && !this->_willFrameSkip ) if ( (isFramebufferRenderNeeded[GPUEngineID_Main] || this->_engineMain->IsForceBlankSet() || isDisplayCaptureNeeded) && !this->_willFrameSkip )
{ {
// GPUEngineA:WillRender3DLayer() and GPUEngineA:WillCapture3DLayerDirect() both rely on register // GPUEngineA:WillRender3DLayer() and GPUEngineA:WillCapture3DLayerDirect() both rely on register
// states that might change on a per-line basis. Therefore, we need to check these states on a // states that might change on a per-line basis. Therefore, we need to check these states on a
@ -5502,7 +5528,7 @@ void GPUSubsystem::RenderLine(const size_t l)
this->_engineMain->UpdatePropertiesWithoutRender(l); this->_engineMain->UpdatePropertiesWithoutRender(l);
} }
if (isFramebufferRenderNeeded[GPUEngineID_Sub] && !this->_willFrameSkip) if ( (isFramebufferRenderNeeded[GPUEngineID_Sub] || this->_engineSub->IsForceBlankSet()) && !this->_willFrameSkip)
{ {
switch (this->_engineSub->GetTargetDisplay()->GetColorFormat()) switch (this->_engineSub->GetTargetDisplay()->GetColorFormat())
{ {

View File

@ -117,7 +117,7 @@ typedef union
u8 OBJ_Tile_mapping:1; // 4: A+B; 0=2D (32KB), 1=1D (32..256KB) u8 OBJ_Tile_mapping:1; // 4: A+B; 0=2D (32KB), 1=1D (32..256KB)
u8 OBJ_BMP_2D_dim:1; // 5: A+B; 0=128x512, 1=256x256 pixels u8 OBJ_BMP_2D_dim:1; // 5: A+B; 0=128x512, 1=256x256 pixels
u8 OBJ_BMP_mapping:1; // 6: A+B; 0=2D (128KB), 1=1D (128..256KB) u8 OBJ_BMP_mapping:1; // 6: A+B; 0=2D (128KB), 1=1D (128..256KB)
u8 ForceBlank:1; // 7: A+B; u8 ForceBlank:1; // 7: A+B; 0=Disable, 1=Enable (causes the line to render all white)
u8 BG0_Enable:1; // 8: A+B; 0=Disable, 1=Enable u8 BG0_Enable:1; // 8: A+B; 0=Disable, 1=Enable
u8 BG1_Enable:1; // 9: A+B; 0=Disable, 1=Enable u8 BG1_Enable:1; // 9: A+B; 0=Disable, 1=Enable
@ -143,7 +143,7 @@ typedef union
u8 ExBGxPalette_Enable:1; // 30: A+B; 0=Disable, 1=Enable BG extended Palette u8 ExBGxPalette_Enable:1; // 30: A+B; 0=Disable, 1=Enable BG extended Palette
u8 ExOBJPalette_Enable:1; // 31: A+B; 0=Disable, 1=Enable OBJ extended Palette u8 ExOBJPalette_Enable:1; // 31: A+B; 0=Disable, 1=Enable OBJ extended Palette
#else #else
u8 ForceBlank:1; // 7: A+B; u8 ForceBlank:1; // 7: A+B; 0=Disable, 1=Enable (causes the line to render all white)
u8 OBJ_BMP_mapping:1; // 6: A+B; 0=2D (128KB), 1=1D (128..256KB) u8 OBJ_BMP_mapping:1; // 6: A+B; 0=2D (128KB), 1=1D (128..256KB)
u8 OBJ_BMP_2D_dim:1; // 5: A+B; 0=128x512, 1=256x256 pixels u8 OBJ_BMP_2D_dim:1; // 5: A+B; 0=128x512, 1=256x256 pixels
u8 OBJ_Tile_mapping:1; // 4: A+B; 0=2D (32KB), 1=1D (32..256KB) u8 OBJ_Tile_mapping:1; // 4: A+B; 0=2D (32KB), 1=1D (32..256KB)
@ -1543,6 +1543,8 @@ protected:
void _RenderLine_SetupSprites(GPUEngineCompositorInfo &compInfo); void _RenderLine_SetupSprites(GPUEngineCompositorInfo &compInfo);
template<NDSColorFormat OUTPUTFORMAT, bool WILLPERFORMWINDOWTEST> void _RenderLine_Layers(GPUEngineCompositorInfo &compInfo); template<NDSColorFormat OUTPUTFORMAT, bool WILLPERFORMWINDOWTEST> void _RenderLine_Layers(GPUEngineCompositorInfo &compInfo);
void _RenderLineBlank(const size_t l);
void _HandleDisplayModeOff(const size_t l); void _HandleDisplayModeOff(const size_t l);
void _HandleDisplayModeNormal(const size_t l); void _HandleDisplayModeNormal(const size_t l);
@ -1609,6 +1611,7 @@ public:
const GPU_IOREG& GetIORegisterMap() const; const GPU_IOREG& GetIORegisterMap() const;
bool IsForceBlankSet() const;
bool IsMasterBrightMaxOrMin() const; bool IsMasterBrightMaxOrMin() const;
bool GetEnableState(); bool GetEnableState();