// Mode 4 is another bitmap mode. It also has a 240×160 frame-buffer, but instead of 16bpp pixels it uses 8bpp pixels. // These 8 bits are a palette index to the background palette located at 0500:0000 (palette 0) private void RenderMode4Scanline() { byte[] vram = gba.Memory.VRam; Color[] palette = Palettes.Palette0; int y = CurrentScanline; bool pixelDrawn; bool windowing = false; bool objVisibleOverride = true; int windowRegion = 0; int bgVisibleOverride = 0; ObjController.ObjPrioritySort(); for (int x = 0; x < Screen_X_Resolution; x++) { pixelDrawn = false; for (int priority = 0; priority < 4; priority++) { // Sprite rendering if (DisplayControlRegister.DisplayObj && (!windowing || (windowing && objVisibleOverride))) { pixelDrawn = ObjController.RenderSpritePixel(drawBuffer, x, y, priority, windowing, windowRegion, ref bgVisibleOverride); if (pixelDrawn) { break; } } } if (pixelDrawn == false) { // 2nd framebuffer is at 0xA000 int index = vram[((y * Screen_X_Resolution) + x)]; drawBuffer.SetPixel(x, y, palette[index]); } } }
// DENNIS: the way I did threaded rendering was by caching the LCD IO registers, and updating them for the PPU whenever it started rendering the next scanline private void RenderScanlineTextMode() { /* if(frameNumber % 30 != 0) { return; } */ /* if (DisplayControlRegister.BgVisible(0)) Bg[0].CacheScanline(); if (DisplayControlRegister.BgVisible(1)) Bg[1].CacheScanline(); if (DisplayControlRegister.BgVisible(2)) Bg[2].CacheScanline(); if (DisplayControlRegister.BgVisible(3)) Bg[3].CacheScanline(); */ ObjController.ObjPrioritySort(); int scanline = CurrentScanline; bool windowing = (DisplayControlRegister.DisplayWin0 || DisplayControlRegister.DisplayWin1 || DisplayControlRegister.DisplayWin1 || DisplayControlRegister.DisplayObjWin); //Bg[0].WaitForScanline(); //Bg[1].WaitForScanline(); //Bg[2].WaitForScanline(); //Bg[3].WaitForScanline(); // We render front to back. Once a pixel is drawn we stop going through the layers. // TODO: In order to do blending we may need to go through all the layers for each pixel #if ParallelizeScanline var paritioner = Partitioner.Create(0, LcdController.Screen_X_Resolution, 80); var result = Parallel.ForEach(paritioner, (range) => { for (int x = range.Item1; x < range.Item2; x++) #else for (int x = 0; x < Screen_X_Resolution; x++) #endif { int paletteIndex; bool pixelDrawn = false; // Windowing can disable obj's and bg's bool objVisibleOverride = false; int windowRegion = 0; int bgVisibleOverride = 0; // bitmask for bg visible (from window) if (windowing) { windowRegion = TileHelpers.PixelWindowRegion(x, CurrentScanline, gba); // 0 is outside of all windows if (windowRegion == 0) { objVisibleOverride = Windows[(int) Window.WindowName.WindowOut].DisplayObjs; bgVisibleOverride = Windows[(int)Window.WindowName.WindowOut].DisplayBg0 | Windows[(int)Window.WindowName.WindowOut].DisplayBg1 | Windows[(int)Window.WindowName.WindowOut].DisplayBg2 | Windows[(int)Window.WindowName.WindowOut].DisplayBg3; } // Window 0 takes priority over window 1. We don't need to check if the point is within both areas as it is done in the function call above else if((windowRegion & (int)TileHelpers.WindowRegion.Window0) != 0) { objVisibleOverride = Windows[(int)Window.WindowName.Window0].DisplayObjs; bgVisibleOverride = Windows[(int)Window.WindowName.Window0].DisplayBg0 | Windows[(int)Window.WindowName.Window0].DisplayBg1 | Windows[(int)Window.WindowName.Window0].DisplayBg2 | Windows[(int)Window.WindowName.Window0].DisplayBg3; } else if ((windowRegion & (int)TileHelpers.WindowRegion.Window1) != 0) { objVisibleOverride = Windows[(int)Window.WindowName.Window1].DisplayObjs; bgVisibleOverride = Windows[(int)Window.WindowName.Window1].DisplayBg0 | Windows[(int)Window.WindowName.Window1].DisplayBg1 | Windows[(int)Window.WindowName.Window1].DisplayBg2 | Windows[(int)Window.WindowName.Window1].DisplayBg3; } } else { objVisibleOverride = true; bgVisibleOverride = 0; } // Start at the top priority, if something draws to the pixel, we can early out and stop processing this pixel // Highest priority (0) is drawn at the front. Lower priorities can be obscured for (int priority = 0; priority < 4; priority++) { // Sprite rendering // If a sprite has the same priority as a bg, the sprite is drawn on top, therefore we check sprites first if (DisplayControlRegister.DisplayObj && (!windowing || (windowing && objVisibleOverride))) { pixelDrawn = ObjController.RenderSpritePixel(drawBuffer, x, scanline, priority, windowing, windowRegion, ref bgVisibleOverride); if (pixelDrawn) { break; } } // No Sprite occupied this pixel, move on to backgrounds // Bg Rendering // Find the backgrounds with this priority // In case of same priority, BG0 has highest (drawn at front) for (int bgSelect = 0; bgSelect < 4; bgSelect++) { if (Bg[bgSelect].CntRegister.Priority != priority || DisplayControlRegister.BgVisible(Bg[bgSelect].BgNumber) == false || (windowing && ((bgVisibleOverride & (1 << bgSelect)) == 0))) { continue; } /* if(Bg[2].AffineMode && bgSelect == 1) { continue; } */ if (Bg[bgSelect].AffineMode) { paletteIndex = Bg[bgSelect].PixelValueAffine(x, scanline); } else { paletteIndex = Bg[bgSelect].PixelValue(x, scanline); } // TODO: If we need to blend then get the pixels we need to blend here /* lock (Bg[bgSelect].ScanlineData) { paletteIndex = Bg[bgSelect].ScanlineData[x]; } */ // Pal 0 == Transparent if (paletteIndex == 0) { continue; } drawBuffer.SetPixel(x, scanline, Palettes.Palette0[paletteIndex]); pixelDrawn = true; // Once a pixel has been drawn, no need to check other BG's break; } if(pixelDrawn) { break; } } // If nothing is drawn then default to backdrop colour if (pixelDrawn == false) { drawBuffer.SetPixel(x, scanline, Palettes.Palette0[0]); } } #if ParallelizeScanline }); // Parallel.For //while (result.IsCompleted == false) ; #endif }