FabGL
ESP32 Display Controller and Graphics Library
vgapalettedcontroller.cpp
1 /*
2  Created by Fabrizio Di Vittorio (fdivitto2013@gmail.com) - <http://www.fabgl.com>
3  Copyright (c) 2019-2021 Fabrizio Di Vittorio.
4  All rights reserved.
5 
6 
7 * Please contact fdivitto2013@gmail.com if you need a commercial license.
8 
9 
10 * This library and related software is available under GPL v3. Feel free to use FabGL in free software and hardware:
11 
12  FabGL is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  FabGL is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with FabGL. If not, see <http://www.gnu.org/licenses/>.
24  */
25 
26 
27 
28 #include <alloca.h>
29 #include <stdarg.h>
30 #include <math.h>
31 #include <string.h>
32 
33 #include "freertos/FreeRTOS.h"
34 #include "freertos/task.h"
35 
36 #include "soc/i2s_struct.h"
37 #include "soc/i2s_reg.h"
38 #include "driver/periph_ctrl.h"
39 #include "soc/rtc.h"
40 #include "esp_spi_flash.h"
41 #include "esp_heap_caps.h"
42 
43 #include "fabutils.h"
44 #include "vgapalettedcontroller.h"
45 #include "devdrivers/swgenerator.h"
46 
47 
48 
49 #pragma GCC optimize ("O2")
50 
51 
52 
53 namespace fabgl {
54 
55 
56 
57 
58 
59 /*************************************************************************************/
60 /* VGAPalettedController definitions */
61 
62 
63 volatile uint8_t * * VGAPalettedController::s_viewPort;
64 volatile uint8_t * * VGAPalettedController::s_viewPortVisible;
65 lldesc_t volatile * VGAPalettedController::s_frameResetDesc;
66 volatile int VGAPalettedController::s_scanLine;
67 
68 
69 
70 
71 VGAPalettedController::VGAPalettedController(int linesCount, NativePixelFormat nativePixelFormat, int viewPortRatioDiv, int viewPortRatioMul, intr_handler_t isrHandler)
72  : m_linesCount(linesCount),
73  m_nativePixelFormat(nativePixelFormat),
74  m_viewPortRatioDiv(viewPortRatioDiv),
75  m_viewPortRatioMul(viewPortRatioMul),
76  m_isrHandler(isrHandler)
77 {
78  m_lines = (volatile uint8_t**) heap_caps_malloc(sizeof(uint8_t*) * m_linesCount, MALLOC_CAP_32BIT | MALLOC_CAP_INTERNAL);
79  m_palette = (RGB222*) heap_caps_malloc(sizeof(RGB222) * getPaletteSize(), MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
80 }
81 
82 
83 VGAPalettedController::~VGAPalettedController()
84 {
85  heap_caps_free(m_palette);
86  heap_caps_free(m_lines);
87 }
88 
89 
90 void VGAPalettedController::init()
91 {
92  VGABaseController::init();
93 
94  m_doubleBufferOverDMA = false;
95  m_taskProcessingPrimitives = false;
96  m_processPrimitivesOnBlank = false;
97  m_primitiveExecTask = nullptr;
98 }
99 
100 
101 void VGAPalettedController::end()
102 {
103  if (m_primitiveExecTask) {
104  vTaskDelete(m_primitiveExecTask);
105  m_primitiveExecTask = nullptr;
106  m_taskProcessingPrimitives = false;
107  }
108  VGABaseController::end();
109 }
110 
111 
113 {
114  VGABaseController::suspendBackgroundPrimitiveExecution();
115  while (m_taskProcessingPrimitives)
116  ;
117 }
118 
119 // make sure view port height is divisible by VGA16_LinesCount
120 void VGAPalettedController::checkViewPortSize()
121 {
122  m_viewPortHeight &= ~(m_linesCount - 1);
123 }
124 
125 
126 void VGAPalettedController::allocateViewPort()
127 {
128  VGABaseController::allocateViewPort(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL, m_viewPortWidth / m_viewPortRatioDiv * m_viewPortRatioMul);
129 
130  for (int i = 0; i < m_linesCount; ++i)
131  m_lines[i] = (uint8_t*) heap_caps_malloc(m_viewPortWidth, MALLOC_CAP_DMA);
132 }
133 
134 
135 void VGAPalettedController::freeViewPort()
136 {
137  VGABaseController::freeViewPort();
138 
139  for (int i = 0; i < m_linesCount; ++i) {
140  heap_caps_free((void*)m_lines[i]);
141  m_lines[i] = nullptr;
142  }
143 }
144 
145 
146 void VGAPalettedController::setResolution(VGATimings const& timings, int viewPortWidth, int viewPortHeight, bool doubleBuffered)
147 {
148  VGABaseController::setResolution(timings, viewPortWidth, viewPortHeight, doubleBuffered);
149 
150  s_viewPort = m_viewPort;
151  s_viewPortVisible = m_viewPortVisible;
152 
153  // fill view port
154  for (int i = 0; i < m_viewPortHeight; ++i)
155  memset((void*)(m_viewPort[i]), 0, m_viewPortWidth / m_viewPortRatioDiv * m_viewPortRatioMul);
156 
157  setupDefaultPalette();
158  updateRGB2PaletteLUT();
159 
160  calculateAvailableCyclesForDrawings();
161 
162  // must be started before interrupt alloc
163  startGPIOStream();
164 
165  // ESP_INTR_FLAG_LEVEL1: should be less than PS2Controller interrupt level, necessary when running on the same core
166  if (m_isr_handle == nullptr) {
167  CoreUsage::setBusiestCore(FABGLIB_VIDEO_CPUINTENSIVE_TASKS_CORE);
168  esp_intr_alloc_pinnedToCore(ETS_I2S1_INTR_SOURCE, ESP_INTR_FLAG_LEVEL1 | ESP_INTR_FLAG_IRAM, m_isrHandler, this, &m_isr_handle, FABGLIB_VIDEO_CPUINTENSIVE_TASKS_CORE);
169  I2S1.int_clr.val = 0xFFFFFFFF;
170  I2S1.int_ena.out_eof = 1;
171  }
172 
173  if (m_primitiveExecTask == nullptr) {
174  xTaskCreatePinnedToCore(primitiveExecTask, "" , FABGLIB_VGAPALETTEDCONTROLLER_PRIMTASK_STACK_SIZE, this, FABGLIB_VGAPALETTEDCONTROLLER_PRIMTASK_PRIORITY, &m_primitiveExecTask, CoreUsage::quietCore());
175  }
176 
178 }
179 
180 
181 void VGAPalettedController::onSetupDMABuffer(lldesc_t volatile * buffer, bool isStartOfVertFrontPorch, int scan, bool isVisible, int visibleRow)
182 {
183  if (isVisible) {
184  buffer->buf = (uint8_t *) m_lines[visibleRow % m_linesCount];
185 
186  // generate interrupt every half m_linesCount
187  if ((scan == 0 && (visibleRow % (m_linesCount / 2)) == 0)) {
188  if (visibleRow == 0)
189  s_frameResetDesc = buffer;
190  buffer->eof = 1;
191  }
192  }
193 }
194 
195 
196 int VGAPalettedController::getPaletteSize()
197 {
198  switch (nativePixelFormat()) {
200  return 2;
202  return 4;
204  return 8;
206  return 16;
207  default:
208  return 0;
209  }
210 }
211 
212 
213 // rebuild m_packedRGB222_to_PaletteIndex
214 void VGAPalettedController::updateRGB2PaletteLUT()
215 {
216  auto paletteSize = getPaletteSize();
217  for (int r = 0; r < 4; ++r)
218  for (int g = 0; g < 4; ++g)
219  for (int b = 0; b < 4; ++b) {
220  double H1, S1, V1;
221  rgb222_to_hsv(r, g, b, &H1, &S1, &V1);
222  int bestIdx = 0;
223  int bestDst = 1000000000;
224  for (int i = 0; i < paletteSize; ++i) {
225  double H2, S2, V2;
226  rgb222_to_hsv(m_palette[i].R, m_palette[i].G, m_palette[i].B, &H2, &S2, &V2);
227  double AH = H1 - H2;
228  double AS = S1 - S2;
229  double AV = V1 - V2;
230  int dst = AH * AH + AS * AS + AV * AV;
231  if (dst <= bestDst) { // "<=" to prioritize higher indexes
232  bestIdx = i;
233  bestDst = dst;
234  if (bestDst == 0)
235  break;
236  }
237  }
238  m_packedRGB222_to_PaletteIndex[r | (g << 2) | (b << 4)] = bestIdx;
239  }
240 }
241 
242 
243 // calculates number of CPU cycles usable to draw primitives
244 void VGAPalettedController::calculateAvailableCyclesForDrawings()
245 {
246  int availtime_us;
247 
248  if (m_processPrimitivesOnBlank) {
249  // allowed time to process primitives is limited to the vertical blank. Slow, but avoid flickering
250  availtime_us = ceil(1000000.0 / m_timings.frequency * m_timings.scanCount * m_HLineSize * (m_linesCount / 2 + m_timings.VFrontPorch + m_timings.VSyncPulse + m_timings.VBackPorch + m_viewPortRow));
251  } else {
252  // allowed time is the half of an entire frame. Fast, but may flick
253  availtime_us = ceil(1000000.0 / m_timings.frequency * m_timings.scanCount * m_HLineSize * (m_timings.VVisibleArea + m_timings.VFrontPorch + m_timings.VSyncPulse + m_timings.VBackPorch));
254  availtime_us /= 2;
255  }
256 
257  m_primitiveExecTimeoutCycles = getCPUFrequencyMHz() * availtime_us; // at 240Mhz, there are 240 cycles every microsecond
258 }
259 
260 
261 // we can use getCycleCount here because primitiveExecTask is pinned to a specific core (so cycle counter is the same)
262 // getCycleCount() requires 0.07us, while esp_timer_get_time() requires 0.78us
263 void VGAPalettedController::primitiveExecTask(void * arg)
264 {
265  auto ctrl = (VGAPalettedController *) arg;
266 
267  while (true) {
268  if (!ctrl->m_primitiveProcessingSuspended) {
269  auto startCycle = ctrl->backgroundPrimitiveTimeoutEnabled() ? getCycleCount() : 0;
270  Rect updateRect = Rect(SHRT_MAX, SHRT_MAX, SHRT_MIN, SHRT_MIN);
271  ctrl->m_taskProcessingPrimitives = true;
272  do {
273  Primitive prim;
274  if (ctrl->getPrimitive(&prim, 0) == false)
275  break;
276  ctrl->execPrimitive(prim, updateRect, false);
277  if (ctrl->m_primitiveProcessingSuspended)
278  break;
279  } while (!ctrl->backgroundPrimitiveTimeoutEnabled() || (startCycle + ctrl->m_primitiveExecTimeoutCycles > getCycleCount()));
280  ctrl->showSprites(updateRect);
281  ctrl->m_taskProcessingPrimitives = false;
282  }
283 
284  // wait for vertical sync
285  ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
286  }
287 
288 }
289 
290 
291 void VGAPalettedController::swapBuffers()
292 {
293  VGABaseController::swapBuffers();
294  s_viewPort = m_viewPort;
295  s_viewPortVisible = m_viewPortVisible;
296 }
297 
298 
299 
300 } // end of namespace
301 
virtual void resumeBackgroundPrimitiveExecution()=0
Resumes drawings after suspendBackgroundPrimitiveExecution().
NativePixelFormat nativePixelFormat()
Represents the native pixel format used by this display.
This file contains fabgl::GPIOStream definition.
uint8_t B
#define FABGLIB_VGAPALETTEDCONTROLLER_PRIMTASK_STACK_SIZE
Definition: fabglconf.h:142
#define FABGLIB_VIDEO_CPUINTENSIVE_TASKS_CORE
Definition: fabglconf.h:138
void suspendBackgroundPrimitiveExecution()
Suspends drawings.
uint8_t G
This file contains some utility classes and functions.
Definition: canvas.cpp:36
This file contains fabgl::VGAPalettedController definition.
NativePixelFormat
This enum defines the display controller native pixel format.
uint8_t R
#define FABGLIB_VGAPALETTEDCONTROLLER_PRIMTASK_PRIORITY
Definition: fabglconf.h:146