1 /* 2 * Copyright (C) 2015-2017, by Laszlo Szeremi under the Boost license. 3 * 4 * Pixel Perfect Engine, graphics.bitmap module 5 */ 6 module pixelperfectengine.graphics.raster; 7 8 import pixelperfectengine.graphics.outputscreen; 9 import pixelperfectengine.graphics.layers; 10 import pixelperfectengine.graphics.bitmap; 11 import bindbc.sdl; 12 public import pixelperfectengine.graphics.common; 13 import std.conv; 14 import std.algorithm.sorting; 15 import std.algorithm.mutation; 16 import core.time; 17 import collections.treemap; 18 19 ///The raster calls it every time it finishes the drawing to the framebuffers. 20 ///Used to signal the output screen to switch out the framebuffers. 21 public interface RefreshListener{ 22 public void refreshFinished(); 23 } 24 25 ///Used to read the output from the raster and show it on the screen. 26 public interface IRaster{ 27 public SDL_Texture* getOutput(); 28 } 29 /** 30 * Defines palette handling functions. 31 * It provides various functions for safe palette handling. 32 */ 33 public interface PaletteContainer { 34 /** 35 * Returns the palette of the object. 36 */ 37 public @property Color[] palette() @safe pure nothrow @nogc; 38 ///Returns the given palette index. 39 public Color getPaletteIndex(ushort index) @safe pure nothrow @nogc const; 40 ///Sets the given palette index to the given value. 41 public Color setPaletteIndex(ushort index, Color value) @safe pure nothrow @nogc; 42 /** 43 * Adds a palette chunk to the end of the main palette. 44 */ 45 public Color[] addPaletteChunk(Color[] paletteChunk) @safe; 46 /** 47 * Loads a palette into the object. 48 * Returns the new palette of the object. 49 */ 50 public Color[] loadPalette(Color[] palette) @safe; 51 /** 52 * Loads a palette chunk into the object. 53 * The `offset` determines where the palette should be loaded. 54 * If it points to an existing place, the indices after that will be overwritten until the whole palette will be copied. 55 * If it points to the end or after it, then the palette will be made longer, and will pad with values #00000000 if needed. 56 * Returns the new palette of the object. 57 */ 58 public Color[] loadPaletteChunk(Color[] paletteChunk, ushort offset) @safe; 59 /** 60 * Clears an area of the palette with zeroes. 61 * Returns the original area. 62 */ 63 public Color[] clearPaletteChunk(ushort lenght, ushort offset) @safe; 64 } 65 66 ///Handles multiple layers onto one framebuffer. 67 public class Raster : IRaster, PaletteContainer{ 68 private ushort rX, rY; ///Stores screen resolution. Set overscan resolutions at OutputWindow 69 //public SDL_Surface* workpad; 70 public SDL_Texture*[] frameBuffer; 71 public void* fbData; 72 public int fbPitch; 73 /** 74 * Color format is ARGB, with each index having their own transparency. 75 */ 76 protected Color[] _palette; 77 alias LayerMap = TreeMap!(int, Layer); 78 ///Stores the layers by their priorities. 79 public LayerMap layerMap; 80 //private Layer[int] layerList; 81 private int[] threads; ///Thread IDs (currently unused). 82 private bool r; ///Set to true if refresh is happening. 83 protected ubyte nOfBuffers; ///Number of framebuffers, 2 for double buffering. 84 protected ubyte updatedBuffer; ///Framebuffer currently being updated 85 protected ubyte displayedBuffer;///Framebuffer currently being displayed 86 //private int[2] doubleBufferRegisters; 87 private RefreshListener[] rL; ///Contains RefreshListeners associated with this raster. 88 private MonoTime frameTime, frameTime_1; ///Timestamps of frame occurences 89 private Duration delta_frameTime; ///Current time delta between two frames 90 private real framesPerSecond, avgFPS; ///Current and average fps counter 91 //public Bitmap16Bit[2] frameBuffer; 92 93 ///Creates a raster with the supplied parameters. 94 ///Params: 95 /// x = Width of the raster. 96 /// y = Height of the raster. 97 /// paletteLength = The initial size of the palette. 98 /// buffers = The number of buffers to be used. Default is two for double buffering. Single buffering can 99 ///eliminate occassional flickering and latency at the possibility of screen tearing and having to wait for 100 ///the buffer to be finished. 101 public this(ushort x, ushort y, OutputScreen oW, size_t paletteLength, ubyte buffers = 2){ 102 //workpad = SDL_CreateRGBSurface(SDL_SWSURFACE, x, y, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); 103 //this.threads = threads; 104 assert(paletteLength <= 65_536); 105 _palette.length = paletteLength; 106 SDL_Renderer* renderer = oW.renderer; 107 rX=x; 108 rY=y; 109 nOfBuffers = buffers; 110 for (int i ; i < buffers ; i++) 111 frameBuffer ~= SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGRX8888, SDL_TEXTUREACCESS_STREAMING, x, y); 112 /+doubleBufferRegisters[0] = 1; 113 doubleBufferRegisters[1] = 0;+/ 114 oW.setMainRaster(this); 115 addRefreshListener(oW); 116 frameTime = MonoTimeImpl!(ClockType.normal).currTime(); 117 framesPerSecond = 0.0; 118 avgFPS = 0.0; 119 } 120 /** 121 * Returns a copy of the palette of the object. 122 */ 123 public @property Color[] palette() @safe pure nothrow @nogc { 124 return _palette; 125 } 126 public Color getPaletteIndex(ushort index) @safe pure nothrow @nogc const { 127 return _palette[index]; 128 } 129 public Color setPaletteIndex(ushort index, Color val) @safe pure nothrow @nogc { 130 return _palette[index] = val; 131 } 132 /** 133 * Loads a palette into the object. 134 * Returns the new palette of the object. 135 */ 136 public Color[] loadPalette(Color[] palette) @safe { 137 return _palette = palette; 138 } 139 /** 140 * Adds a palette chunk to the end of the main palette. 141 */ 142 public Color[] addPaletteChunk(Color[] paletteChunk) @safe { 143 return _palette ~= paletteChunk; 144 } 145 /** 146 * Loads a palette chunk into the object. 147 * The offset determines where the palette should be loaded. 148 * If it points to an existing place, the indices after that will be overwritten until the whole palette will be copied. 149 * If it points to the end or after it, then the palette will be made longer, and will pad with values 0x00_00_00_00 if needed. 150 * Returns the new palette of the object. 151 */ 152 public Color[] loadPaletteChunk(Color[] paletteChunk, ushort offset) @safe { 153 if (paletteChunk.length + offset > _palette.length) 154 _palette.length = offset + paletteChunk.length; 155 for (int i = offset, j ; j < paletteChunk.length ; i++, j++) 156 _palette[i] = paletteChunk[j]; 157 return _palette; 158 } 159 /** 160 * Clears an area of the palette with zeroes. 161 * Returns the original area. 162 */ 163 public Color[] clearPaletteChunk(ushort lenght, ushort offset) @safe { 164 Color[] backup = _palette[offset..offset + lenght].dup; 165 for (int i = offset ; i < offset + lenght ; i++) { 166 _palette[i] = Color(0); 167 } 168 return backup; 169 } 170 /** 171 * Returns the current FPS count. 172 */ 173 public @property real fps() @safe @nogc pure nothrow const { 174 return framesPerSecond; 175 } 176 /** 177 * Returns the current average FPS count. 178 */ 179 public @property real avgfps() @safe @nogc pure nothrow const { 180 return avgFPS; 181 } 182 /** 183 * Resets the avgFPS to zero. 184 */ 185 public void resetAvgfps() @safe @nogc pure nothrow { 186 avgFPS = 0; 187 } 188 ~this(){ 189 foreach(SDL_Texture* t ; frameBuffer){ 190 if(t) 191 SDL_DestroyTexture(t); 192 } 193 } 194 ///Adds a RefreshListener to its list. 195 public void addRefreshListener(RefreshListener r){ 196 rL ~= r; 197 } 198 ///Sets the number of colors. 199 public void setupPalette(int i) { 200 _palette.length = i; 201 } 202 ///Adds a layer at the given priority. 203 public void addLayer(Layer l, int i) @safe pure nothrow { 204 l.setRasterizer(rX, rY); 205 layerMap[i] = l; 206 } 207 public void loadLayers(R)(R layerRange) @safe pure nothrow { 208 foreach (int key, Layer value; layerRange) { 209 addLayer(value, key); 210 } 211 } 212 ///Removes a layer at the given priority. 213 public void removeLayer(int n) @safe pure nothrow { 214 layerMap.remove(n); 215 } 216 public Layer getLayer(int n) @nogc @safe pure nothrow { 217 return layerMap[n]; 218 } 219 /** 220 * Refreshes the whole framebuffer. 221 */ 222 public void refresh() { 223 import std.stdio : writeln; 224 r = true; 225 226 //get frame duration 227 frameTime_1 = frameTime; 228 frameTime = MonoTimeImpl!(ClockType.normal).currTime(); 229 delta_frameTime = frameTime - frameTime_1; 230 const real delta_frameTime0 = cast(real)(delta_frameTime.total!"usecs"()); 231 framesPerSecond = 1 / (delta_frameTime0 / 1_000_000); 232 if(avgFPS) 233 avgFPS = (avgFPS + framesPerSecond) / 2; 234 else 235 avgFPS = framesPerSecond; 236 237 updatedBuffer++; 238 if(updatedBuffer >= nOfBuffers) updatedBuffer = 0; 239 240 SDL_LockTexture(frameBuffer[updatedBuffer], null, &fbData, &fbPitch); 241 242 /+for(int i ; i < layerPriorityHandler.length ; i++){ 243 layerList[i].updateRaster(fbData, fbPitch, palette.ptr); 244 }+/ 245 foreach (Layer layer ; layerMap) { 246 layer.updateRaster(fbData, fbPitch, palette.ptr); 247 } 248 249 SDL_UnlockTexture(frameBuffer[updatedBuffer]); 250 r = false; 251 252 foreach(r; rL){ 253 r.refreshFinished; 254 } 255 256 } 257 258 259 ///Returns the workpad. 260 public SDL_Texture* getOutput() @nogc @safe pure nothrow { 261 if (displayedBuffer == updatedBuffer) displayedBuffer++; 262 if (displayedBuffer >= nOfBuffers) displayedBuffer = 0; 263 return frameBuffer[displayedBuffer++]; 264 //return frameBuffer[0]; 265 } 266 267 268 ///Returns if the raster is refreshing. 269 public bool isRefreshing(){ 270 return r; 271 } 272 }