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.
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, int columnsQuantum, NativePixelFormat nativePixelFormat, int viewPortRatioDiv, int viewPortRatioMul, intr_handler_t isrHandler)
72  : m_linesCount(linesCount),
73  m_columnsQuantum(columnsQuantum),
74  m_nativePixelFormat(nativePixelFormat),
75  m_viewPortRatioDiv(viewPortRatioDiv),
76  m_viewPortRatioMul(viewPortRatioMul),
77  m_isrHandler(isrHandler)
78 {
79  m_lines = (volatile uint8_t**) heap_caps_malloc(sizeof(uint8_t*) * m_linesCount, MALLOC_CAP_32BIT | MALLOC_CAP_INTERNAL);
80  m_palette = (RGB222*) heap_caps_malloc(sizeof(RGB222) * getPaletteSize(), MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
81 }
82 
83 
84 VGAPalettedController::~VGAPalettedController()
85 {
86  heap_caps_free(m_palette);
87  heap_caps_free(m_lines);
88 }
89 
90 
91 void VGAPalettedController::init()
92 {
93  VGABaseController::init();
94 
95  m_doubleBufferOverDMA = false;
96  m_taskProcessingPrimitives = false;
97  m_processPrimitivesOnBlank = false;
98  m_primitiveExecTask = nullptr;
99 }
100 
101 
102 void VGAPalettedController::end()
103 {
104  if (m_primitiveExecTask) {
105  vTaskDelete(m_primitiveExecTask);
106  m_primitiveExecTask = nullptr;
107  m_taskProcessingPrimitives = false;
108  }
109  VGABaseController::end();
110 }
111 
112 
114 {
115  VGABaseController::suspendBackgroundPrimitiveExecution();
116  while (m_taskProcessingPrimitives)
117  ;
118 }
119 
120 // make sure view port height is divisible by m_linesCount, view port width is divisible by m_columnsQuantum
121 void VGAPalettedController::checkViewPortSize()
122 {
123  m_viewPortHeight &= ~(m_linesCount - 1);
124  m_viewPortWidth &= ~(m_columnsQuantum - 1);
125 }
126 
127 
128 void VGAPalettedController::allocateViewPort()
129 {
130  VGABaseController::allocateViewPort(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL, m_viewPortWidth / m_viewPortRatioDiv * m_viewPortRatioMul);
131 
132  for (int i = 0; i < m_linesCount; ++i)
133  m_lines[i] = (uint8_t*) heap_caps_malloc(m_viewPortWidth, MALLOC_CAP_DMA);
134 }
135 
136 
137 void VGAPalettedController::freeViewPort()
138 {
139  VGABaseController::freeViewPort();
140 
141  for (int i = 0; i < m_linesCount; ++i) {
142  heap_caps_free((void*)m_lines[i]);
143  m_lines[i] = nullptr;
144  }
145 }
146 
147 
148 void VGAPalettedController::setResolution(VGATimings const& timings, int viewPortWidth, int viewPortHeight, bool doubleBuffered)
149 {
150  VGABaseController::setResolution(timings, viewPortWidth, viewPortHeight, doubleBuffered);
151 
152  s_viewPort = m_viewPort;
153  s_viewPortVisible = m_viewPortVisible;
154 
155  // fill view port
156  for (int i = 0; i < m_viewPortHeight; ++i)
157  memset((void*)(m_viewPort[i]), 0, m_viewPortWidth / m_viewPortRatioDiv * m_viewPortRatioMul);
158 
159  setupDefaultPalette();
160  updateRGB2PaletteLUT();
161 
162  calculateAvailableCyclesForDrawings();
163 
164  // must be started before interrupt alloc
165  startGPIOStream();
166 
167  // ESP_INTR_FLAG_LEVEL1: should be less than PS2Controller interrupt level, necessary when running on the same core
168  if (m_isr_handle == nullptr) {
169  CoreUsage::setBusiestCore(FABGLIB_VIDEO_CPUINTENSIVE_TASKS_CORE);
170  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);
171  I2S1.int_clr.val = 0xFFFFFFFF;
172  I2S1.int_ena.out_eof = 1;
173  }
174 
175  if (m_primitiveExecTask == nullptr) {
176  xTaskCreatePinnedToCore(primitiveExecTask, "" , FABGLIB_VGAPALETTEDCONTROLLER_PRIMTASK_STACK_SIZE, this, FABGLIB_VGAPALETTEDCONTROLLER_PRIMTASK_PRIORITY, &m_primitiveExecTask, CoreUsage::quietCore());
177  }
178 
180 }
181 
182 
183 void VGAPalettedController::onSetupDMABuffer(lldesc_t volatile * buffer, bool isStartOfVertFrontPorch, int scan, bool isVisible, int visibleRow)
184 {
185  if (isVisible) {
186  buffer->buf = (uint8_t *) m_lines[visibleRow % m_linesCount];
187 
188  // generate interrupt every half m_linesCount
189  if ((scan == 0 && (visibleRow % (m_linesCount / 2)) == 0)) {
190  if (visibleRow == 0)
191  s_frameResetDesc = buffer;
192  buffer->eof = 1;
193  }
194  }
195 }
196 
197 
198 int VGAPalettedController::getPaletteSize()
199 {
200  switch (nativePixelFormat()) {
202  return 2;
204  return 4;
206  return 8;
208  return 16;
209  default:
210  return 0;
211  }
212 }
213 
214 
215 // rebuild m_packedRGB222_to_PaletteIndex
216 void VGAPalettedController::updateRGB2PaletteLUT()
217 {
218  auto paletteSize = getPaletteSize();
219  for (int r = 0; r < 4; ++r)
220  for (int g = 0; g < 4; ++g)
221  for (int b = 0; b < 4; ++b) {
222  double H1, S1, V1;
223  rgb222_to_hsv(r, g, b, &H1, &S1, &V1);
224  int bestIdx = 0;
225  int bestDst = 1000000000;
226  for (int i = 0; i < paletteSize; ++i) {
227  double H2, S2, V2;
228  rgb222_to_hsv(m_palette[i].R, m_palette[i].G, m_palette[i].B, &H2, &S2, &V2);
229  double AH = H1 - H2;
230  double AS = S1 - S2;
231  double AV = V1 - V2;
232  int dst = AH * AH + AS * AS + AV * AV;
233  if (dst <= bestDst) { // "<=" to prioritize higher indexes
234  bestIdx = i;
235  bestDst = dst;
236  if (bestDst == 0)
237  break;
238  }
239  }
240  m_packedRGB222_to_PaletteIndex[r | (g << 2) | (b << 4)] = bestIdx;
241  }
242 }
243 
244 
245 // calculates number of CPU cycles usable to draw primitives
246 void VGAPalettedController::calculateAvailableCyclesForDrawings()
247 {
248  int availtime_us;
249 
250  if (m_processPrimitivesOnBlank) {
251  // allowed time to process primitives is limited to the vertical blank. Slow, but avoid flickering
252  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));
253  } else {
254  // allowed time is the half of an entire frame. Fast, but may flick
255  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));
256  availtime_us /= 2;
257  }
258 
259  m_primitiveExecTimeoutCycles = getCPUFrequencyMHz() * availtime_us; // at 240Mhz, there are 240 cycles every microsecond
260 }
261 
262 
263 // we can use getCycleCount here because primitiveExecTask is pinned to a specific core (so cycle counter is the same)
264 // getCycleCount() requires 0.07us, while esp_timer_get_time() requires 0.78us
265 void VGAPalettedController::primitiveExecTask(void * arg)
266 {
267  auto ctrl = (VGAPalettedController *) arg;
268 
269  while (true) {
270  if (!ctrl->m_primitiveProcessingSuspended) {
271  auto startCycle = ctrl->backgroundPrimitiveTimeoutEnabled() ? getCycleCount() : 0;
272  Rect updateRect = Rect(SHRT_MAX, SHRT_MAX, SHRT_MIN, SHRT_MIN);
273  ctrl->m_taskProcessingPrimitives = true;
274  do {
275  Primitive prim;
276  if (ctrl->getPrimitive(&prim, 0) == false)
277  break;
278  ctrl->execPrimitive(prim, updateRect, false);
279  if (ctrl->m_primitiveProcessingSuspended)
280  break;
281  } while (!ctrl->backgroundPrimitiveTimeoutEnabled() || (startCycle + ctrl->m_primitiveExecTimeoutCycles > getCycleCount()));
282  ctrl->showSprites(updateRect);
283  ctrl->m_taskProcessingPrimitives = false;
284  }
285 
286  // wait for vertical sync
287  ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
288  }
289 
290 }
291 
292 
293 void VGAPalettedController::swapBuffers()
294 {
295  VGABaseController::swapBuffers();
296  s_viewPort = m_viewPort;
297  s_viewPortVisible = m_viewPortVisible;
298 }
299 
300 
301 
302 } // end of namespace
303 
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