Fixed logic for clearing bottom layer. Added new functions to 2D renderer interface. Avoid drawing top and bottom surfaces when no layers are present there. Added some comments to tile

This commit is contained in:
Bart Trzynadlowski 2016-05-08 19:27:08 +00:00
parent 5d048958b9
commit 9249eaa29f
5 changed files with 143 additions and 37 deletions

View file

@ -28,7 +28,6 @@
* ---------- * ----------
* - Is there a better way to handle the overscan regions in wide screen mode? * - Is there a better way to handle the overscan regions in wide screen mode?
* Is clearing two thin viewports better than one big clear? * Is clearing two thin viewports better than one big clear?
* - Layer priorities in Spikeout attract mode might not be totally correct.
* - Are v-scroll values 9 or 10 bits? (Does it matter?) Lost World seems to * - Are v-scroll values 9 or 10 bits? (Does it matter?) Lost World seems to
* have some scrolling issues. * have some scrolling issues.
* - A proper shut-down function is needed! OpenGL might not be available when * - A proper shut-down function is needed! OpenGL might not be available when
@ -72,8 +71,12 @@
* *
* Bits 'pqrs' control the color depth of layers B', B, A', and A, * Bits 'pqrs' control the color depth of layers B', B, A', and A,
* respectively. If set, the layer's pattern data is encoded as 4 bits, * respectively. If set, the layer's pattern data is encoded as 4 bits,
* otherwise the pixels are 8 bits. Bits 'tuvw' form a 4-bit priority code. The * otherwise the pixels are 8 bits.
* other bits are unused or unknown. *
* Bits 'tuvw' control priority for layers B', B, A', and A, respectively,
* which is also the relative ordering of the layers from bottom to top. For
* each layer, if its bit is clear, it will be drawn below the 3D layer,
* otherwise it is drawn on top.
* *
* The remaining registers are described where appropriate further below. * The remaining registers are described where appropriate further below.
* *
@ -416,16 +419,19 @@ static void DrawLayer(uint32_t *pixels, int layerNum, const uint32_t *vram, cons
int pixelOffset = -hFine; int pixelOffset = -hFine;
int extraTile = (hFine != 0) ? 1 : 0; // h-scrolling requires part of 63rd tile int extraTile = (hFine != 0) ? 1 : 0; // h-scrolling requires part of 63rd tile
// First tile may be clipped
int tx = 0; int tx = 0;
DrawTileLine<bits, alphaTest, true>(line, pixelOffset, nameTable[(hTile ^ 1) & 63], vFine, vram, palette, mask); DrawTileLine<bits, alphaTest, true>(line, pixelOffset, nameTable[(hTile ^ 1) & 63], vFine, vram, palette, mask);
++hTile; ++hTile;
pixelOffset += 8; pixelOffset += 8;
// Middle tiles will not be clipped
for (tx = 1; tx < (62 - 1 + extraTile); tx++) for (tx = 1; tx < (62 - 1 + extraTile); tx++)
{ {
DrawTileLine<bits, alphaTest, false>(line, pixelOffset, nameTable[(hTile ^ 1) & 63], vFine, vram, palette, mask); DrawTileLine<bits, alphaTest, false>(line, pixelOffset, nameTable[(hTile ^ 1) & 63], vFine, vram, palette, mask);
++hTile; ++hTile;
pixelOffset += 8; pixelOffset += 8;
} }
// Last tile may be clipped
DrawTileLine<bits, alphaTest, true>(line, pixelOffset, nameTable[(hTile ^ 1) & 63], vFine, vram, palette, mask); DrawTileLine<bits, alphaTest, true>(line, pixelOffset, nameTable[(hTile ^ 1) & 63], vFine, vram, palette, mask);
++hTile; ++hTile;
pixelOffset += 8; pixelOffset += 8;
@ -436,20 +442,22 @@ static void DrawLayer(uint32_t *pixels, int layerNum, const uint32_t *vram, cons
} }
} }
bool CRender2D::DrawTilemaps(uint32_t *pixelsBottom, uint32_t *pixelsTop) std::pair<bool, bool> CRender2D::DrawTilemaps(uint32_t *pixelsBottom, uint32_t *pixelsTop)
{ {
unsigned priority = (m_regs[0x20/4] >> 8) & 0xF; unsigned priority = (m_regs[0x20/4] >> 8) & 0xF;
// Render bottom layers // Render bottom layers
bool nothingDrawn = true; bool noBottomSurface = true;
for (int layerNum = 3; layerNum >= 0; layerNum--) static const int bottomOrder[4] = { 3, 2, 1, 0 };
for (int i = 0; i < 4; i++)
{ {
int layerNum = bottomOrder[i];
bool is4Bit = (m_regs[0x20/4] & (1 << (12 + layerNum))) != 0; bool is4Bit = (m_regs[0x20/4] & (1 << (12 + layerNum))) != 0;
bool enabled = (m_regs[0x60/4 + layerNum] & 0x80000000) != 0; bool enabled = (m_regs[0x60/4 + layerNum] & 0x80000000) != 0;
bool selected = (priority & (1 << layerNum)) == 0; bool selected = (priority & (1 << layerNum)) == 0;
if (enabled && selected) if (enabled && selected)
{ {
if (nothingDrawn) if (noBottomSurface)
{ {
if (is4Bit) if (is4Bit)
DrawLayer<4, false>(pixelsBottom, layerNum, m_vram, m_regs, m_palette[layerNum / 2]); DrawLayer<4, false>(pixelsBottom, layerNum, m_vram, m_regs, m_palette[layerNum / 2]);
@ -463,23 +471,25 @@ bool CRender2D::DrawTilemaps(uint32_t *pixelsBottom, uint32_t *pixelsTop)
else else
DrawLayer<8, true>(pixelsBottom, layerNum, m_vram, m_regs, m_palette[layerNum / 2]); DrawLayer<8, true>(pixelsBottom, layerNum, m_vram, m_regs, m_palette[layerNum / 2]);
} }
nothingDrawn = false; noBottomSurface = false;
} }
} }
if (nothingDrawn)
ClearLayer(pixelsBottom);
// Render top layers // Render top layers
nothingDrawn = true; // NOTE: layer ordering is different according to MAME (which has 3, 2, 0, 1
for (int layerNum = 3; layerNum >= 0; layerNum--) // for top layer). Until I see evidence that this is correct and not a typo,
// I will assume consistent layer ordering.
bool noTopSurface = true;
static const int topOrder[4] = { 3, 2, 1, 0 };
for (int i = 0; i < 4; i++)
{ {
int layerNum = topOrder[i];
bool is4Bit = (m_regs[0x20/4] & (1 << (12 + layerNum))) != 0; bool is4Bit = (m_regs[0x20/4] & (1 << (12 + layerNum))) != 0;
bool enabled = (m_regs[0x60/4 + layerNum] & 0x80000000) != 0; bool enabled = (m_regs[0x60/4 + layerNum] & 0x80000000) != 0;
bool selected = (priority & (1 << layerNum)) != 0; bool selected = (priority & (1 << layerNum)) != 0;
if (enabled && selected) if (enabled && selected)
{ {
if (nothingDrawn) if (noTopSurface)
{ {
if (is4Bit) if (is4Bit)
DrawLayer<4, false>(pixelsTop, layerNum, m_vram, m_regs, m_palette[layerNum / 2]); DrawLayer<4, false>(pixelsTop, layerNum, m_vram, m_regs, m_palette[layerNum / 2]);
@ -493,15 +503,12 @@ bool CRender2D::DrawTilemaps(uint32_t *pixelsBottom, uint32_t *pixelsTop)
else else
DrawLayer<8, true>(pixelsTop, layerNum, m_vram, m_regs, m_palette[layerNum / 2]); DrawLayer<8, true>(pixelsTop, layerNum, m_vram, m_regs, m_palette[layerNum / 2]);
} }
nothingDrawn = false; noTopSurface = false;
} }
} }
if (nothingDrawn) // Indicate whether top and bottom surfaces have to be rendered
ClearLayer(pixelsTop); return std::pair<bool, bool>(!noTopSurface, !noBottomSurface);
// Indicate whether color buffer must be cleared because of no bottom layer
return nothingDrawn;
} }
@ -566,30 +573,48 @@ void CRender2D::Setup2D(bool isBottom, bool clearAll)
glLoadIdentity(); glLoadIdentity();
} }
// Bottom layers
void CRender2D::BeginFrame(void) void CRender2D::BeginFrame(void)
{ {
// Update all layers }
bool clear = DrawTilemaps(m_bottomSurface, m_topSurface);
glActiveTexture(GL_TEXTURE0); // texture unit 0
glBindTexture(GL_TEXTURE_2D, m_texID[0]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 496, 384, GL_RGBA, GL_UNSIGNED_BYTE, m_topSurface);
glBindTexture(GL_TEXTURE_2D, m_texID[1]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 496, 384, GL_RGBA, GL_UNSIGNED_BYTE, m_bottomSurface);
// Display bottom surface void CRender2D::PreRenderFrame(void)
Setup2D(true, clear); {
if (!clear) // Update all layers
m_surfaces_present = DrawTilemaps(m_bottomSurface, m_topSurface);
glActiveTexture(GL_TEXTURE0); // texture unit 0
if (m_surfaces_present.first)
{
glBindTexture(GL_TEXTURE_2D, m_texID[0]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 496, 384, GL_RGBA, GL_UNSIGNED_BYTE, m_topSurface);
}
if (m_surfaces_present.second)
{
glBindTexture(GL_TEXTURE_2D, m_texID[1]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 496, 384, GL_RGBA, GL_UNSIGNED_BYTE, m_bottomSurface);
}
}
void CRender2D::RenderFrameBottom(void)
{
// Display bottom surface if anything was drawn there, else clear everything
Setup2D(true, m_surfaces_present.second == false);
if (m_surfaces_present.second)
DisplaySurface(1, 0.0); DisplaySurface(1, 0.0);
} }
// Top layers void CRender2D::RenderFrameTop(void)
{
// Display top surface only if it exists
if (m_surfaces_present.first)
{
Setup2D(false, false);
glEnable(GL_BLEND);
DisplaySurface(0, -0.5);
}
}
void CRender2D::EndFrame(void) void CRender2D::EndFrame(void)
{ {
// Display top surface
Setup2D(false, false);
glEnable(GL_BLEND);
DisplaySurface(0, -0.5);
} }

View file

@ -50,6 +50,31 @@ public:
*/ */
void BeginFrame(void); void BeginFrame(void);
/*
* PreRenderFrame(void):
*
* Draws the all top layers (above 3D graphics) and bottom layers (below 3D
* graphics) but does not yet display them. May send data to the GPU.
*/
void PreRenderFrame(void);
/*
* RenderFrameBottom(void):
*
* Overwrites the color buffer with bottom surface that was pre-rendered by
* the last call to PreRenderFrame().
*/
void RenderFrameBottom(void);
/*
* RenderFrameTop(void):
*
* Draws the top surface (if it exists) that was pre-rendered by the last
* call to PreRenderFrame(). Previously drawn graphics layers will be visible
* through transparent regions.
*/
void RenderFrameTop(void);
/* /*
* EndFrame(void): * EndFrame(void):
* *
@ -144,7 +169,7 @@ public:
private: private:
// Private member functions // Private member functions
bool DrawTilemaps(uint32_t *destBottom, uint32_t *destTop); std::pair<bool, bool> DrawTilemaps(uint32_t *destBottom, uint32_t *destTop);
void DisplaySurface(int surface, GLfloat z); void DisplaySurface(int surface, GLfloat z);
void Setup2D(bool isBottom, bool clearAll); void Setup2D(bool isBottom, bool clearAll);
@ -169,6 +194,9 @@ private:
GLuint m_fragmentShader; // fragment shader GLuint m_fragmentShader; // fragment shader
GLuint m_textureMapLoc; // location of "textureMap" uniform GLuint m_textureMapLoc; // location of "textureMap" uniform
// PreRenderFrame() tracks which surfaces exist in current frame
std::pair<bool, bool> m_surfaces_present = std::pair<bool, bool>(false, false);
// Buffers // Buffers
uint8_t *m_memoryPool = 0; // all memory is allocated here uint8_t *m_memoryPool = 0; // all memory is allocated here
uint32_t *m_topSurface = 0; // 512x384x32bpp pixel surface for top layers uint32_t *m_topSurface = 0; // 512x384x32bpp pixel surface for top layers

View file

@ -2139,7 +2139,10 @@ void CModel3::RenderFrame(void)
// Render frame // Render frame
TileGen.BeginFrame(); TileGen.BeginFrame();
GPU.BeginFrame(); GPU.BeginFrame();
TileGen.PreRenderFrame();
TileGen.RenderFrameBottom();
GPU.RenderFrame(); GPU.RenderFrame();
TileGen.RenderFrameTop();
GPU.EndFrame(); GPU.EndFrame();
TileGen.EndFrame(); TileGen.EndFrame();
} }

View file

@ -243,6 +243,21 @@ void CTileGen::BeginFrame(void)
Render2D->BeginFrame(); Render2D->BeginFrame();
} }
void CTileGen::PreRenderFrame(void)
{
Render2D->PreRenderFrame();
}
void CTileGen::RenderFrameBottom(void)
{
Render2D->RenderFrameBottom();
}
void CTileGen::RenderFrameTop(void)
{
Render2D->RenderFrameTop();
}
void CTileGen::EndFrame(void) void CTileGen::EndFrame(void)
{ {
Render2D->EndFrame(); Render2D->EndFrame();

View file

@ -88,16 +88,51 @@ public:
* *
* Prepares to render a new frame. Must be called once per frame prior to * Prepares to render a new frame. Must be called once per frame prior to
* drawing anything and must only access read-only snapshots and variables * drawing anything and must only access read-only snapshots and variables
* since it may be running in a separate thread. * since it may be running in a separate thread.
*
* Invokes the underlying 2D renderer.
*/ */
void BeginFrame(void); void BeginFrame(void);
/*
* PreRenderFrame(void):
*
* Draws the all top layers (above 3D graphics) and bottom layers (below 3D
* graphics) but does not yet display them. May send data to the GPU.
*
* Invokes the equivalent method in the underlying 2D renderer.
*/
void PreRenderFrame(void);
/*
* RenderFrameBottom(void):
*
* Overwrites the color buffer with bottom surface that was pre-rendered by
* the last call to PreRenderFrame().
*
* Invokes the equivalent method in the underlying 2D renderer.
*/
void RenderFrameBottom(void);
/*
* RenderFrameTop(void):
*
* Draws the top surface (if it exists) that was pre-rendered by the last
* call to PreRenderFrame(). Previously drawn graphics layers will be visible
* through transparent regions.
*
* Invokes the equivalent method in the underlying 2D renderer.
*/
void RenderFrameTop(void);
/* /*
* EndFrame(void): * EndFrame(void):
* *
* Signals the end of rendering for this frame. Must be called last during * Signals the end of rendering for this frame. Must be called last during
* the frame and must only access read-only snapshots and variables since it * the frame and must only access read-only snapshots and variables since it
* may be running in a separate thread. * may be running in a separate thread.
*
* Invokes the equivalent method in the underlying 2D renderer.
*/ */
void EndFrame(void); void EndFrame(void);