File: | root/firefox-clang/gfx/wr/swgl/src/gl.cc |
Warning: | line 773, column 8 Excessive padding in 'struct Context' (38 padding bytes, where 6 is optimal). Optimal fields order: blendcolor, cleardepth, queries, framebuffers, renderbuffers, shaders, buffers, programs, textures, vertex_arrays, references, last_error, blendfunc_srgb, blendfunc_drgb, blendfunc_sa, blendfunc_da, blend_equation, depthfunc, unpack_row_length, shaded_rows, shaded_pixels, active_texture_unit, current_program, current_vertex_array, pixel_pack_buffer_binding, pixel_unpack_buffer_binding, array_buffer_binding, time_elapsed_query, samples_passed_query, renderbuffer_binding, draw_framebuffer_binding, read_framebuffer_binding, unknown_binding, viewport, scissor, clearcolor, texture_units, blend, blend_key, depthtest, depthmask, scissortest, validate_vertex_array, consider reordering the fields or adding explicit padding members |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
4 | |
5 | #include <stdlib.h> |
6 | #include <stdint.h> |
7 | #include <string.h> |
8 | #include <assert.h> |
9 | #include <stdio.h> |
10 | #include <math.h> |
11 | |
12 | #ifdef __MACH__ |
13 | # include <mach/mach.h> |
14 | # include <mach/mach_time.h> |
15 | #else |
16 | # include <time.h> |
17 | #endif |
18 | |
19 | #ifdef NDEBUG |
20 | # define debugf(...)printf(...) |
21 | #else |
22 | # define debugf(...)printf(...) printf(__VA_ARGS__) |
23 | #endif |
24 | |
25 | // #define PRINT_TIMINGS |
26 | |
27 | #ifdef _WIN32 |
28 | # define ALWAYS_INLINE__attribute__((always_inline)) inline __forceinline |
29 | # define NO_INLINE__attribute__((noinline)) __declspec(noinline) |
30 | |
31 | // Including Windows.h brings a huge amount of namespace polution so just |
32 | // define a couple of things manually |
33 | typedef int BOOL; |
34 | # define WINAPI __stdcall |
35 | # define DECLSPEC_IMPORT __declspec(dllimport) |
36 | # define WINBASEAPI DECLSPEC_IMPORT |
37 | typedef unsigned long DWORD; |
38 | typedef long LONG; |
39 | typedef __int64 LONGLONG; |
40 | # define DUMMYSTRUCTNAME |
41 | |
42 | typedef union _LARGE_INTEGER { |
43 | struct { |
44 | DWORD LowPart; |
45 | LONG HighPart; |
46 | } DUMMYSTRUCTNAME; |
47 | struct { |
48 | DWORD LowPart; |
49 | LONG HighPart; |
50 | } u; |
51 | LONGLONG QuadPart; |
52 | } LARGE_INTEGER; |
53 | extern "C" { |
54 | WINBASEAPI BOOL WINAPI |
55 | QueryPerformanceCounter(LARGE_INTEGER* lpPerformanceCount); |
56 | |
57 | WINBASEAPI BOOL WINAPI QueryPerformanceFrequency(LARGE_INTEGER* lpFrequency); |
58 | } |
59 | |
60 | #else |
61 | // GCC is slower when dealing with always_inline, especially in debug builds. |
62 | // When using Clang, use always_inline more aggressively. |
63 | # if defined(__clang__1) || defined(NDEBUG) |
64 | # define ALWAYS_INLINE__attribute__((always_inline)) inline __attribute__((always_inline)) inline |
65 | # else |
66 | # define ALWAYS_INLINE__attribute__((always_inline)) inline inline |
67 | # endif |
68 | # define NO_INLINE__attribute__((noinline)) __attribute__((noinline)) |
69 | #endif |
70 | |
71 | // Some functions may cause excessive binary bloat if inlined in debug or with |
72 | // GCC builds, so use PREFER_INLINE on these instead of ALWAYS_INLINE. |
73 | #if defined(__clang__1) && defined(NDEBUG) |
74 | # define PREFER_INLINEinline ALWAYS_INLINE__attribute__((always_inline)) inline |
75 | #else |
76 | # define PREFER_INLINEinline inline |
77 | #endif |
78 | |
79 | #define UNREACHABLE__builtin_unreachable() __builtin_unreachable() |
80 | |
81 | #define UNUSED[[maybe_unused]] [[maybe_unused]] |
82 | |
83 | #define FALLTHROUGH[[fallthrough]] [[fallthrough]] |
84 | |
85 | #if defined(MOZILLA_CLIENT1) && defined(MOZ_CLANG_PLUGIN) |
86 | # define IMPLICIT __attribute__((annotate("moz_implicit"))) |
87 | #else |
88 | # define IMPLICIT |
89 | #endif |
90 | |
91 | #include "gl_defs.h" |
92 | #include "glsl.h" |
93 | #include "program.h" |
94 | #include "texture.h" |
95 | |
96 | using namespace glsl; |
97 | |
98 | typedef ivec2_scalar IntPoint; |
99 | |
100 | struct IntRect { |
101 | int x0; |
102 | int y0; |
103 | int x1; |
104 | int y1; |
105 | |
106 | IntRect() : x0(0), y0(0), x1(0), y1(0) {} |
107 | IntRect(int x0, int y0, int x1, int y1) : x0(x0), y0(y0), x1(x1), y1(y1) {} |
108 | IntRect(IntPoint origin, IntPoint size) |
109 | : x0(origin.x), |
110 | y0(origin.y), |
111 | x1(origin.x + size.x), |
112 | y1(origin.y + size.y) {} |
113 | |
114 | int width() const { return x1 - x0; } |
115 | int height() const { return y1 - y0; } |
116 | bool is_empty() const { return width() <= 0 || height() <= 0; } |
117 | |
118 | IntPoint origin() const { return IntPoint(x0, y0); } |
119 | |
120 | bool same_size(const IntRect& o) const { |
121 | return width() == o.width() && height() == o.height(); |
122 | } |
123 | |
124 | bool contains(const IntRect& o) const { |
125 | return o.x0 >= x0 && o.y0 >= y0 && o.x1 <= x1 && o.y1 <= y1; |
126 | } |
127 | |
128 | IntRect& intersect(const IntRect& o) { |
129 | x0 = max(x0, o.x0); |
130 | y0 = max(y0, o.y0); |
131 | x1 = min(x1, o.x1); |
132 | y1 = min(y1, o.y1); |
133 | return *this; |
134 | } |
135 | |
136 | IntRect intersection(const IntRect& o) { |
137 | IntRect result = *this; |
138 | result.intersect(o); |
139 | return result; |
140 | } |
141 | |
142 | // Scale from source-space to dest-space, optionally rounding inward |
143 | IntRect& scale(int srcWidth, int srcHeight, int dstWidth, int dstHeight, |
144 | bool roundIn = false) { |
145 | x0 = (x0 * dstWidth + (roundIn ? srcWidth - 1 : 0)) / srcWidth; |
146 | y0 = (y0 * dstHeight + (roundIn ? srcHeight - 1 : 0)) / srcHeight; |
147 | x1 = (x1 * dstWidth) / srcWidth; |
148 | y1 = (y1 * dstHeight) / srcHeight; |
149 | return *this; |
150 | } |
151 | |
152 | // Flip the rect's Y coords around inflection point at Y=offset |
153 | void invert_y(int offset) { |
154 | y0 = offset - y0; |
155 | y1 = offset - y1; |
156 | swap(y0, y1); |
157 | } |
158 | |
159 | IntRect& offset(const IntPoint& o) { |
160 | x0 += o.x; |
161 | y0 += o.y; |
162 | x1 += o.x; |
163 | y1 += o.y; |
164 | return *this; |
165 | } |
166 | |
167 | IntRect operator+(const IntPoint& o) const { |
168 | return IntRect(*this).offset(o); |
169 | } |
170 | IntRect operator-(const IntPoint& o) const { |
171 | return IntRect(*this).offset(-o); |
172 | } |
173 | }; |
174 | |
175 | typedef vec2_scalar Point2D; |
176 | typedef vec4_scalar Point3D; |
177 | |
178 | struct IntRange { |
179 | int start; |
180 | int end; |
181 | |
182 | int len() const { return end - start; } |
183 | |
184 | IntRange intersect(IntRange r) const { |
185 | return {max(start, r.start), min(end, r.end)}; |
186 | } |
187 | }; |
188 | |
189 | struct FloatRange { |
190 | float start; |
191 | float end; |
192 | |
193 | float clip(float x) const { return clamp(x, start, end); } |
194 | |
195 | FloatRange clip(FloatRange r) const { return {clip(r.start), clip(r.end)}; } |
196 | |
197 | FloatRange merge(FloatRange r) const { |
198 | return {min(start, r.start), max(end, r.end)}; |
199 | } |
200 | |
201 | IntRange round__glsl_round() const { |
202 | return {int(floor__glsl_floor(start + 0.5f)), int(floor__glsl_floor(end + 0.5f))}; |
203 | } |
204 | |
205 | IntRange round_out() const { return {int(floor__glsl_floor(start)), int(ceil__glsl_ceil(end))}; } |
206 | }; |
207 | |
208 | template <typename P> |
209 | static inline FloatRange x_range(P p0, P p1) { |
210 | return {min(p0.x, p1.x), max(p0.x, p1.x)}; |
211 | } |
212 | |
213 | struct VertexAttrib { |
214 | size_t size = 0; // in bytes |
215 | GLenum type = 0; |
216 | bool normalized = false; |
217 | GLsizei stride = 0; |
218 | GLuint offset = 0; |
219 | bool enabled = false; |
220 | GLuint divisor = 0; |
221 | int vertex_array = 0; |
222 | int vertex_buffer = 0; |
223 | char* buf = nullptr; // XXX: this can easily dangle |
224 | size_t buf_size = 0; // this will let us bounds check |
225 | |
226 | // Mark the buffer as invalid so we don't accidentally use stale data. |
227 | void disable() { |
228 | enabled = false; |
229 | buf = nullptr; |
230 | buf_size = 0; |
231 | } |
232 | }; |
233 | |
234 | static int bytes_for_internal_format(GLenum internal_format) { |
235 | switch (internal_format) { |
236 | case GL_RGBA32F0x8814: |
237 | return 4 * 4; |
238 | case GL_RGBA32I0x8D82: |
239 | case GL_RGBA_INTEGER0x8D99: |
240 | return 4 * 4; |
241 | case GL_RGBA80x8058: |
242 | case GL_BGRA80x93A1: |
243 | case GL_RGBA0x1908: |
244 | case GL_BGRA0x80E1: |
245 | return 4; |
246 | case GL_R80x8229: |
247 | case GL_RED0x1903: |
248 | return 1; |
249 | case GL_RG80x822B: |
250 | case GL_RG0x8227: |
251 | return 2; |
252 | case GL_DEPTH_COMPONENT0x1902: |
253 | case GL_DEPTH_COMPONENT160x81A5: |
254 | case GL_DEPTH_COMPONENT240x81A6: |
255 | case GL_DEPTH_COMPONENT320x81A7: |
256 | return 4; |
257 | case GL_RGB_RAW_422_APPLE0x8A51: |
258 | return 2; |
259 | case GL_R160x822A: |
260 | return 2; |
261 | case GL_RG160x822C: |
262 | return 4; |
263 | default: |
264 | debugf("internal format: %x\n", internal_format)printf("internal format: %x\n", internal_format); |
265 | assert(0)(static_cast <bool> (0) ? void (0) : __assert_fail ("0" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
266 | return 0; |
267 | } |
268 | } |
269 | |
270 | static inline int aligned_stride(int row_bytes) { return (row_bytes + 3) & ~3; } |
271 | |
272 | static TextureFormat gl_format_to_texture_format(int type) { |
273 | switch (type) { |
274 | case GL_RGBA32F0x8814: |
275 | return TextureFormat::RGBA32F; |
276 | case GL_RGBA32I0x8D82: |
277 | return TextureFormat::RGBA32I; |
278 | case GL_RGBA80x8058: |
279 | return TextureFormat::RGBA8; |
280 | case GL_R80x8229: |
281 | return TextureFormat::R8; |
282 | case GL_RG80x822B: |
283 | return TextureFormat::RG8; |
284 | case GL_R160x822A: |
285 | return TextureFormat::R16; |
286 | case GL_RG160x822C: |
287 | return TextureFormat::RG16; |
288 | case GL_RGB_RAW_422_APPLE0x8A51: |
289 | return TextureFormat::YUY2; |
290 | default: |
291 | assert(0)(static_cast <bool> (0) ? void (0) : __assert_fail ("0" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
292 | return TextureFormat::RGBA8; |
293 | } |
294 | } |
295 | |
296 | struct Query { |
297 | uint64_t value = 0; |
298 | }; |
299 | |
300 | struct Buffer { |
301 | char* buf = nullptr; |
302 | size_t size = 0; |
303 | size_t capacity = 0; |
304 | |
305 | // Returns true if re-allocation succeeded, false otherwise... |
306 | bool allocate(size_t new_size) { |
307 | // If the size remains unchanged, don't allocate anything. |
308 | if (new_size == size) { |
309 | return true; |
310 | } |
311 | // If the new size is within the existing capacity of the buffer, just |
312 | // reuse the existing buffer. |
313 | if (new_size <= capacity) { |
314 | size = new_size; |
315 | return true; |
316 | } |
317 | // Otherwise we need to reallocate the buffer to hold up to the requested |
318 | // larger size. |
319 | char* new_buf = (char*)realloc(buf, new_size); |
320 | assert(new_buf)(static_cast <bool> (new_buf) ? void (0) : __assert_fail ("new_buf", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
321 | if (!new_buf) { |
322 | // If we fail, null out the buffer rather than leave around the old |
323 | // allocation state. |
324 | cleanup(); |
325 | return false; |
326 | } |
327 | // The reallocation succeeded, so install the buffer. |
328 | buf = new_buf; |
329 | size = new_size; |
330 | capacity = new_size; |
331 | return true; |
332 | } |
333 | |
334 | void cleanup() { |
335 | if (buf) { |
336 | free(buf); |
337 | buf = nullptr; |
338 | size = 0; |
339 | capacity = 0; |
340 | } |
341 | } |
342 | |
343 | ~Buffer() { cleanup(); } |
344 | |
345 | char* end_ptr() const { return buf ? buf + size : nullptr; } |
346 | |
347 | void* get_data(void* data) { |
348 | if (buf) { |
349 | size_t offset = (size_t)data; |
350 | if (offset < size) { |
351 | return buf + offset; |
352 | } |
353 | } |
354 | return nullptr; |
355 | } |
356 | }; |
357 | |
358 | struct Framebuffer { |
359 | GLuint color_attachment = 0; |
360 | GLuint depth_attachment = 0; |
361 | }; |
362 | |
363 | struct Renderbuffer { |
364 | GLuint texture = 0; |
365 | |
366 | void on_erase(); |
367 | }; |
368 | |
369 | TextureFilter gl_filter_to_texture_filter(int type) { |
370 | switch (type) { |
371 | case GL_NEAREST0x2600: |
372 | return TextureFilter::NEAREST; |
373 | case GL_NEAREST_MIPMAP_LINEAR0x2702: |
374 | return TextureFilter::NEAREST; |
375 | case GL_NEAREST_MIPMAP_NEAREST0x2700: |
376 | return TextureFilter::NEAREST; |
377 | case GL_LINEAR0x2601: |
378 | return TextureFilter::LINEAR; |
379 | case GL_LINEAR_MIPMAP_LINEAR0x2703: |
380 | return TextureFilter::LINEAR; |
381 | case GL_LINEAR_MIPMAP_NEAREST0x2701: |
382 | return TextureFilter::LINEAR; |
383 | default: |
384 | assert(0)(static_cast <bool> (0) ? void (0) : __assert_fail ("0" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
385 | return TextureFilter::NEAREST; |
386 | } |
387 | } |
388 | |
389 | struct Texture { |
390 | GLenum internal_format = 0; |
391 | int width = 0; |
392 | int height = 0; |
393 | char* buf = nullptr; |
394 | size_t buf_size = 0; |
395 | uint32_t buf_stride = 0; |
396 | uint8_t buf_bpp = 0; |
397 | GLenum min_filter = GL_NEAREST0x2600; |
398 | GLenum mag_filter = GL_LINEAR0x2601; |
399 | // The number of active locks on this texture. If this texture has any active |
400 | // locks, we need to disallow modifying or destroying the texture as it may |
401 | // be accessed by other threads where modifications could lead to races. |
402 | int32_t locked = 0; |
403 | // When used as an attachment of a framebuffer, rendering to the texture |
404 | // behaves as if it is located at the given offset such that the offset is |
405 | // subtracted from all transformed vertexes after the viewport is applied. |
406 | IntPoint offset; |
407 | |
408 | enum FLAGS { |
409 | // If the buffer is internally-allocated by SWGL |
410 | SHOULD_FREE = 1 << 1, |
411 | // If the buffer has been cleared to initialize it. Currently this is only |
412 | // utilized by depth buffers which need to know when depth runs have reset |
413 | // to a valid row state. When unset, the depth runs may contain garbage. |
414 | CLEARED = 1 << 2, |
415 | // The texture was deleted while still locked and must stay alive until all |
416 | // locks are released. |
417 | ZOMBIE = 1 << 3, |
418 | }; |
419 | int flags = SHOULD_FREE; |
420 | bool should_free() const { return bool(flags & SHOULD_FREE); } |
421 | bool cleared() const { return bool(flags & CLEARED); } |
422 | bool zombie() const { return bool(flags & ZOMBIE); } |
423 | |
424 | void set_flag(int flag, bool val) { |
425 | if (val) { |
426 | flags |= flag; |
427 | } else { |
428 | flags &= ~flag; |
429 | } |
430 | } |
431 | void set_should_free(bool val) { |
432 | // buf must be null before SHOULD_FREE can be safely toggled. Otherwise, we |
433 | // might accidentally mistakenly realloc an externally allocated buffer as |
434 | // if it were an internally allocated one. |
435 | assert(!buf)(static_cast <bool> (!buf) ? void (0) : __assert_fail ( "!buf", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
436 | set_flag(SHOULD_FREE, val); |
437 | } |
438 | void set_cleared(bool val) { set_flag(CLEARED, val); } |
439 | void set_zombie(bool val) { set_flag(ZOMBIE, val); } |
440 | |
441 | // Delayed-clearing state. When a clear of an FB is requested, we don't |
442 | // immediately clear each row, as the rows may be subsequently overwritten |
443 | // by draw calls, allowing us to skip the work of clearing the affected rows |
444 | // either fully or partially. Instead, we keep a bit vector of rows that need |
445 | // to be cleared later and save the value they need to be cleared with so |
446 | // that we can clear these rows individually when they are touched by draws. |
447 | // This currently only works for 2D textures, but not on texture arrays. |
448 | int delay_clear = 0; |
449 | uint32_t clear_val = 0; |
450 | uint32_t* cleared_rows = nullptr; |
451 | |
452 | void init_depth_runs(uint32_t z); |
453 | void fill_depth_runs(uint32_t z, const IntRect& scissor); |
454 | |
455 | void enable_delayed_clear(uint32_t val) { |
456 | delay_clear = height; |
457 | clear_val = val; |
458 | if (!cleared_rows) { |
459 | cleared_rows = new uint32_t[(height + 31) / 32]; |
460 | } |
461 | memset(cleared_rows, 0, ((height + 31) / 32) * sizeof(uint32_t)); |
462 | if (height & 31) { |
463 | cleared_rows[height / 32] = ~0U << (height & 31); |
464 | } |
465 | } |
466 | |
467 | void disable_delayed_clear() { |
468 | if (cleared_rows) { |
469 | delete[] cleared_rows; |
470 | cleared_rows = nullptr; |
471 | delay_clear = 0; |
472 | } |
473 | } |
474 | |
475 | int bpp() const { return buf_bpp; } |
476 | int compute_bpp() const { return bytes_for_internal_format(internal_format); } |
477 | |
478 | size_t stride() const { return buf_stride; } |
479 | size_t compute_stride(int bpp, int width) const { |
480 | return aligned_stride(bpp * width); |
481 | } |
482 | |
483 | // Set an external backing buffer of this texture. |
484 | void set_buffer(void* new_buf, size_t new_stride) { |
485 | assert(!should_free())(static_cast <bool> (!should_free()) ? void (0) : __assert_fail ("!should_free()", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
486 | // Ensure that the supplied stride is at least as big as the row data and |
487 | // is aligned to the smaller of either the BPP or word-size. We need to at |
488 | // least be able to sample data from within a row and sample whole pixels |
489 | // of smaller formats without risking unaligned access. |
490 | int new_bpp = compute_bpp(); |
491 | assert(new_stride >= size_t(new_bpp * width) &&(static_cast <bool> (new_stride >= size_t(new_bpp * width ) && new_stride % min(new_bpp, sizeof(uint32_t)) == 0 ) ? void (0) : __assert_fail ("new_stride >= size_t(new_bpp * width) && new_stride % min(new_bpp, sizeof(uint32_t)) == 0" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )) |
492 | new_stride % min(new_bpp, sizeof(uint32_t)) == 0)(static_cast <bool> (new_stride >= size_t(new_bpp * width ) && new_stride % min(new_bpp, sizeof(uint32_t)) == 0 ) ? void (0) : __assert_fail ("new_stride >= size_t(new_bpp * width) && new_stride % min(new_bpp, sizeof(uint32_t)) == 0" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
493 | |
494 | buf = (char*)new_buf; |
495 | buf_size = 0; |
496 | buf_bpp = new_bpp; |
497 | buf_stride = new_stride; |
498 | } |
499 | |
500 | // Returns true if re-allocation succeeded, false otherwise... |
501 | bool allocate(bool force = false, int min_width = 0, int min_height = 0) { |
502 | assert(!locked)(static_cast <bool> (!locked) ? void (0) : __assert_fail ("!locked", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); // Locked textures shouldn't be reallocated |
503 | // If we get here, some GL API call that invalidates the texture was used. |
504 | // Mark the buffer as not-cleared to signal this. |
505 | set_cleared(false); |
506 | // Check if there is either no buffer currently or if we forced validation |
507 | // of the buffer size because some dimension might have changed. |
508 | if ((!buf || force) && should_free()) { |
509 | // Compute the buffer's BPP and stride, since they may have changed. |
510 | int new_bpp = compute_bpp(); |
511 | size_t new_stride = compute_stride(new_bpp, width); |
512 | // Compute new size based on the maximum potential stride, rather than |
513 | // the current stride, to hopefully avoid reallocations when size would |
514 | // otherwise change too much... |
515 | size_t max_stride = compute_stride(new_bpp, max(width, min_width)); |
516 | size_t size = max_stride * max(height, min_height); |
517 | if ((!buf && size > 0) || size > buf_size) { |
518 | // Allocate with a SIMD register-sized tail of padding at the end so we |
519 | // can safely read or write past the end of the texture with SIMD ops. |
520 | // Currently only the flat Z-buffer texture needs this padding due to |
521 | // full-register loads and stores in check_depth and discard_depth. In |
522 | // case some code in the future accidentally uses a linear filter on a |
523 | // texture with less than 2 pixels per row, we also add this padding |
524 | // just to be safe. All other texture types and use-cases should be |
525 | // safe to omit padding. |
526 | size_t padding = |
527 | internal_format == GL_DEPTH_COMPONENT240x81A6 || max(width, min_width) < 2 |
528 | ? sizeof(Float) |
529 | : 0; |
530 | char* new_buf = (char*)realloc(buf, size + padding); |
531 | assert(new_buf)(static_cast <bool> (new_buf) ? void (0) : __assert_fail ("new_buf", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
532 | if (!new_buf) { |
533 | // Allocation failed, so ensure we don't leave stale buffer state. |
534 | cleanup(); |
535 | return false; |
536 | } |
537 | // Successfully reallocated the buffer, so go ahead and set it. |
538 | buf = new_buf; |
539 | buf_size = size; |
540 | } |
541 | // Set the BPP and stride in case they changed. |
542 | buf_bpp = new_bpp; |
543 | buf_stride = new_stride; |
544 | } |
545 | // Allocation succeeded or nothing changed... |
546 | return true; |
547 | } |
548 | |
549 | void cleanup() { |
550 | assert(!locked)(static_cast <bool> (!locked) ? void (0) : __assert_fail ("!locked", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); // Locked textures shouldn't be destroyed |
551 | if (buf) { |
552 | // If we need to toggle SHOULD_FREE state, ensure that buf is nulled out, |
553 | // regardless of whether we internally allocated it. This will prevent us |
554 | // from wrongly treating buf as having been internally allocated for when |
555 | // we go to realloc if it actually was externally allocted. |
556 | if (should_free()) { |
557 | free(buf); |
558 | } |
559 | buf = nullptr; |
560 | buf_size = 0; |
561 | buf_bpp = 0; |
562 | buf_stride = 0; |
563 | } |
564 | disable_delayed_clear(); |
565 | } |
566 | |
567 | ~Texture() { cleanup(); } |
568 | |
569 | IntRect bounds() const { return IntRect{0, 0, width, height}; } |
570 | IntRect offset_bounds() const { return bounds() + offset; } |
571 | |
572 | // Find the valid sampling bounds relative to the requested region |
573 | IntRect sample_bounds(const IntRect& req, bool invertY = false) const { |
574 | IntRect bb = bounds().intersect(req) - req.origin(); |
575 | if (invertY) bb.invert_y(req.height()); |
576 | return bb; |
577 | } |
578 | |
579 | // Get a pointer for sampling at the given offset |
580 | char* sample_ptr(int x, int y) const { |
581 | return buf + y * stride() + x * bpp(); |
582 | } |
583 | |
584 | // Get a pointer to the end of the current buffer |
585 | char* end_ptr() const { |
586 | return buf + (height - 1) * stride() + width * bpp(); |
587 | } |
588 | |
589 | // Get a pointer for sampling the requested region and limit to the provided |
590 | // sampling bounds |
591 | char* sample_ptr(const IntRect& req, const IntRect& bounds, |
592 | bool invertY = false) const { |
593 | // Offset the sample pointer by the clamped bounds |
594 | int x = req.x0 + bounds.x0; |
595 | // Invert the Y offset if necessary |
596 | int y = invertY ? req.y1 - 1 - bounds.y0 : req.y0 + bounds.y0; |
597 | return sample_ptr(x, y); |
598 | } |
599 | }; |
600 | |
601 | // The last vertex attribute is reserved as a null attribute in case a vertex |
602 | // attribute is used without being set. |
603 | #define MAX_ATTRIBS17 17 |
604 | #define NULL_ATTRIB16 16 |
605 | struct VertexArray { |
606 | VertexAttrib attribs[MAX_ATTRIBS17]; |
607 | int max_attrib = -1; |
608 | // The GL spec defines element array buffer binding to be part of VAO state. |
609 | GLuint element_array_buffer_binding = 0; |
610 | |
611 | void validate(); |
612 | }; |
613 | |
614 | struct Shader { |
615 | GLenum type = 0; |
616 | ProgramLoader loader = nullptr; |
617 | }; |
618 | |
619 | struct Program { |
620 | ProgramImpl* impl = nullptr; |
621 | VertexShaderImpl* vert_impl = nullptr; |
622 | FragmentShaderImpl* frag_impl = nullptr; |
623 | bool deleted = false; |
624 | |
625 | ~Program() { delete impl; } |
626 | }; |
627 | |
628 | // clang-format off |
629 | // Fully-expand GL defines while ignoring more than 4 suffixes |
630 | #define CONCAT_KEY(prefix, x, y, z, w, ...)prefixxyzw prefix##x##y##z##w |
631 | // Generate a blend key enum symbol |
632 | #define BLEND_KEY(...)BLEND_...000 CONCAT_KEY(BLEND_, __VA_ARGS__, 0, 0, 0)BLEND___VA_ARGS__000 |
633 | #define MASK_BLEND_KEY(...)MASK_BLEND_...000 CONCAT_KEY(MASK_BLEND_, __VA_ARGS__, 0, 0, 0)MASK_BLEND___VA_ARGS__000 |
634 | #define AA_BLEND_KEY(...)AA_BLEND_...000 CONCAT_KEY(AA_BLEND_, __VA_ARGS__, 0, 0, 0)AA_BLEND___VA_ARGS__000 |
635 | #define AA_MASK_BLEND_KEY(...)AA_MASK_BLEND_...000 CONCAT_KEY(AA_MASK_BLEND_, __VA_ARGS__, 0, 0, 0)AA_MASK_BLEND___VA_ARGS__000 |
636 | |
637 | // Utility macro to easily generate similar code for all implemented blend modes |
638 | #define FOR_EACH_BLEND_KEY(macro)macro(1, 0, 0, 0) macro(0x0302, 0x0303, 1, 0x0303) macro(1, 0x0303 , 0, 0) macro(0, 0x0301, 0, 0) macro(0, 0x0301, 0, 1) macro(0 , 0x0303, 0, 0) macro(0, 0x0300, 0, 0) macro(1, 1, 0, 0) macro (1, 1, 1, 0x0303) macro(0x0305, 1, 0, 1) macro(0x8001, 0x0301 , 0, 0) macro(1, 0x88FA, 0, 0) macro(0x8007, 0, 0, 0) macro(0x8008 , 0, 0, 0) macro(0x9294, 0, 0, 0) macro(0x9295, 0, 0, 0) macro (0x9296, 0, 0, 0) macro(0x9297, 0, 0, 0) macro(0x9298, 0, 0, 0 ) macro(0x9299, 0, 0, 0) macro(0x929A, 0, 0, 0) macro(0x929B, 0, 0, 0) macro(0x929C, 0, 0, 0) macro(0x929E, 0, 0, 0) macro (0x92A0, 0, 0, 0) macro(0x92AD, 0, 0, 0) macro(0x92AE, 0, 0, 0 ) macro(0x92AF, 0, 0, 0) macro(0x92B0, 0, 0, 0) macro(0xB001, 0, 0, 0) macro(0xB002, 0, 0, 0) \ |
639 | macro(GL_ONE1, GL_ZERO0, 0, 0) \ |
640 | macro(GL_SRC_ALPHA0x0302, GL_ONE_MINUS_SRC_ALPHA0x0303, GL_ONE1, GL_ONE_MINUS_SRC_ALPHA0x0303) \ |
641 | macro(GL_ONE1, GL_ONE_MINUS_SRC_ALPHA0x0303, 0, 0) \ |
642 | macro(GL_ZERO0, GL_ONE_MINUS_SRC_COLOR0x0301, 0, 0) \ |
643 | macro(GL_ZERO0, GL_ONE_MINUS_SRC_COLOR0x0301, GL_ZERO0, GL_ONE1) \ |
644 | macro(GL_ZERO0, GL_ONE_MINUS_SRC_ALPHA0x0303, 0, 0) \ |
645 | macro(GL_ZERO0, GL_SRC_COLOR0x0300, 0, 0) \ |
646 | macro(GL_ONE1, GL_ONE1, 0, 0) \ |
647 | macro(GL_ONE1, GL_ONE1, GL_ONE1, GL_ONE_MINUS_SRC_ALPHA0x0303) \ |
648 | macro(GL_ONE_MINUS_DST_ALPHA0x0305, GL_ONE1, GL_ZERO0, GL_ONE1) \ |
649 | macro(GL_CONSTANT_COLOR0x8001, GL_ONE_MINUS_SRC_COLOR0x0301, 0, 0) \ |
650 | macro(GL_ONE1, GL_ONE_MINUS_SRC1_COLOR0x88FA, 0, 0) \ |
651 | macro(GL_MIN0x8007, 0, 0, 0) \ |
652 | macro(GL_MAX0x8008, 0, 0, 0) \ |
653 | macro(GL_MULTIPLY_KHR0x9294, 0, 0, 0) \ |
654 | macro(GL_SCREEN_KHR0x9295, 0, 0, 0) \ |
655 | macro(GL_OVERLAY_KHR0x9296, 0, 0, 0) \ |
656 | macro(GL_DARKEN_KHR0x9297, 0, 0, 0) \ |
657 | macro(GL_LIGHTEN_KHR0x9298, 0, 0, 0) \ |
658 | macro(GL_COLORDODGE_KHR0x9299, 0, 0, 0) \ |
659 | macro(GL_COLORBURN_KHR0x929A, 0, 0, 0) \ |
660 | macro(GL_HARDLIGHT_KHR0x929B, 0, 0, 0) \ |
661 | macro(GL_SOFTLIGHT_KHR0x929C, 0, 0, 0) \ |
662 | macro(GL_DIFFERENCE_KHR0x929E, 0, 0, 0) \ |
663 | macro(GL_EXCLUSION_KHR0x92A0, 0, 0, 0) \ |
664 | macro(GL_HSL_HUE_KHR0x92AD, 0, 0, 0) \ |
665 | macro(GL_HSL_SATURATION_KHR0x92AE, 0, 0, 0) \ |
666 | macro(GL_HSL_COLOR_KHR0x92AF, 0, 0, 0) \ |
667 | macro(GL_HSL_LUMINOSITY_KHR0x92B0, 0, 0, 0) \ |
668 | macro(SWGL_BLEND_DROP_SHADOW0xB001, 0, 0, 0) \ |
669 | macro(SWGL_BLEND_SUBPIXEL_TEXT0xB002, 0, 0, 0) |
670 | |
671 | #define DEFINE_BLEND_KEY(...)BLEND_...000, BLEND_KEY(__VA_ARGS__)BLEND___VA_ARGS__000, |
672 | #define DEFINE_MASK_BLEND_KEY(...)MASK_BLEND_...000, MASK_BLEND_KEY(__VA_ARGS__)MASK_BLEND___VA_ARGS__000, |
673 | #define DEFINE_AA_BLEND_KEY(...)AA_BLEND_...000, AA_BLEND_KEY(__VA_ARGS__)AA_BLEND___VA_ARGS__000, |
674 | #define DEFINE_AA_MASK_BLEND_KEY(...)AA_MASK_BLEND_...000, AA_MASK_BLEND_KEY(__VA_ARGS__)AA_MASK_BLEND___VA_ARGS__000, |
675 | enum BlendKey : uint8_t { |
676 | FOR_EACH_BLEND_KEY(DEFINE_BLEND_KEY)BLEND_1000, BLEND_0x03020x030310x0303, BLEND_10x030300, BLEND_00x030100 , BLEND_00x030101, BLEND_00x030300, BLEND_00x030000, BLEND_1100 , BLEND_1110x0303, BLEND_0x0305101, BLEND_0x80010x030100, BLEND_10x88FA00 , BLEND_0x8007000, BLEND_0x8008000, BLEND_0x9294000, BLEND_0x9295000 , BLEND_0x9296000, BLEND_0x9297000, BLEND_0x9298000, BLEND_0x9299000 , BLEND_0x929A000, BLEND_0x929B000, BLEND_0x929C000, BLEND_0x929E000 , BLEND_0x92A0000, BLEND_0x92AD000, BLEND_0x92AE000, BLEND_0x92AF000 , BLEND_0x92B0000, BLEND_0xB001000, BLEND_0xB002000, |
677 | FOR_EACH_BLEND_KEY(DEFINE_MASK_BLEND_KEY)MASK_BLEND_1000, MASK_BLEND_0x03020x030310x0303, MASK_BLEND_10x030300 , MASK_BLEND_00x030100, MASK_BLEND_00x030101, MASK_BLEND_00x030300 , MASK_BLEND_00x030000, MASK_BLEND_1100, MASK_BLEND_1110x0303 , MASK_BLEND_0x0305101, MASK_BLEND_0x80010x030100, MASK_BLEND_10x88FA00 , MASK_BLEND_0x8007000, MASK_BLEND_0x8008000, MASK_BLEND_0x9294000 , MASK_BLEND_0x9295000, MASK_BLEND_0x9296000, MASK_BLEND_0x9297000 , MASK_BLEND_0x9298000, MASK_BLEND_0x9299000, MASK_BLEND_0x929A000 , MASK_BLEND_0x929B000, MASK_BLEND_0x929C000, MASK_BLEND_0x929E000 , MASK_BLEND_0x92A0000, MASK_BLEND_0x92AD000, MASK_BLEND_0x92AE000 , MASK_BLEND_0x92AF000, MASK_BLEND_0x92B0000, MASK_BLEND_0xB001000 , MASK_BLEND_0xB002000, |
678 | FOR_EACH_BLEND_KEY(DEFINE_AA_BLEND_KEY)AA_BLEND_1000, AA_BLEND_0x03020x030310x0303, AA_BLEND_10x030300 , AA_BLEND_00x030100, AA_BLEND_00x030101, AA_BLEND_00x030300, AA_BLEND_00x030000, AA_BLEND_1100, AA_BLEND_1110x0303, AA_BLEND_0x0305101 , AA_BLEND_0x80010x030100, AA_BLEND_10x88FA00, AA_BLEND_0x8007000 , AA_BLEND_0x8008000, AA_BLEND_0x9294000, AA_BLEND_0x9295000, AA_BLEND_0x9296000, AA_BLEND_0x9297000, AA_BLEND_0x9298000, AA_BLEND_0x9299000 , AA_BLEND_0x929A000, AA_BLEND_0x929B000, AA_BLEND_0x929C000, AA_BLEND_0x929E000, AA_BLEND_0x92A0000, AA_BLEND_0x92AD000, AA_BLEND_0x92AE000 , AA_BLEND_0x92AF000, AA_BLEND_0x92B0000, AA_BLEND_0xB001000, AA_BLEND_0xB002000, |
679 | FOR_EACH_BLEND_KEY(DEFINE_AA_MASK_BLEND_KEY)AA_MASK_BLEND_1000, AA_MASK_BLEND_0x03020x030310x0303, AA_MASK_BLEND_10x030300 , AA_MASK_BLEND_00x030100, AA_MASK_BLEND_00x030101, AA_MASK_BLEND_00x030300 , AA_MASK_BLEND_00x030000, AA_MASK_BLEND_1100, AA_MASK_BLEND_1110x0303 , AA_MASK_BLEND_0x0305101, AA_MASK_BLEND_0x80010x030100, AA_MASK_BLEND_10x88FA00 , AA_MASK_BLEND_0x8007000, AA_MASK_BLEND_0x8008000, AA_MASK_BLEND_0x9294000 , AA_MASK_BLEND_0x9295000, AA_MASK_BLEND_0x9296000, AA_MASK_BLEND_0x9297000 , AA_MASK_BLEND_0x9298000, AA_MASK_BLEND_0x9299000, AA_MASK_BLEND_0x929A000 , AA_MASK_BLEND_0x929B000, AA_MASK_BLEND_0x929C000, AA_MASK_BLEND_0x929E000 , AA_MASK_BLEND_0x92A0000, AA_MASK_BLEND_0x92AD000, AA_MASK_BLEND_0x92AE000 , AA_MASK_BLEND_0x92AF000, AA_MASK_BLEND_0x92B0000, AA_MASK_BLEND_0xB001000 , AA_MASK_BLEND_0xB002000, |
680 | BLEND_KEY_NONE = BLEND_KEY(GL_ONE, GL_ZERO)BLEND_1000, |
681 | MASK_BLEND_KEY_NONE = MASK_BLEND_KEY(GL_ONE, GL_ZERO)MASK_BLEND_1000, |
682 | AA_BLEND_KEY_NONE = AA_BLEND_KEY(GL_ONE, GL_ZERO)AA_BLEND_1000, |
683 | AA_MASK_BLEND_KEY_NONE = AA_MASK_BLEND_KEY(GL_ONE, GL_ZERO)AA_MASK_BLEND_1000, |
684 | }; |
685 | // clang-format on |
686 | |
687 | const size_t MAX_TEXTURE_UNITS = 16; |
688 | |
689 | template <typename T> |
690 | static inline bool unlink(T& binding, T n) { |
691 | if (binding == n) { |
692 | binding = 0; |
693 | return true; |
694 | } |
695 | return false; |
696 | } |
697 | |
698 | template <typename O> |
699 | struct ObjectStore { |
700 | O** objects = nullptr; |
701 | size_t size = 0; |
702 | // reserve object 0 as null |
703 | size_t first_free = 1; |
704 | O invalid; |
705 | |
706 | ~ObjectStore() { |
707 | if (objects) { |
708 | for (size_t i = 0; i < size; i++) delete objects[i]; |
709 | free(objects); |
710 | } |
711 | } |
712 | |
713 | bool grow(size_t i) { |
714 | size_t new_size = size ? size : 8; |
715 | while (new_size <= i) new_size += new_size / 2; |
716 | O** new_objects = (O**)realloc(objects, new_size * sizeof(O*)); |
717 | assert(new_objects)(static_cast <bool> (new_objects) ? void (0) : __assert_fail ("new_objects", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
718 | if (!new_objects) return false; |
719 | while (size < new_size) new_objects[size++] = nullptr; |
720 | objects = new_objects; |
721 | return true; |
722 | } |
723 | |
724 | void insert(size_t i, const O& o) { |
725 | if (i >= size && !grow(i)) return; |
726 | if (!objects[i]) objects[i] = new O(o); |
727 | } |
728 | |
729 | size_t next_free() { |
730 | size_t i = first_free; |
731 | while (i < size && objects[i]) i++; |
732 | first_free = i; |
733 | return i; |
734 | } |
735 | |
736 | size_t insert(const O& o = O()) { |
737 | size_t i = next_free(); |
738 | insert(i, o); |
739 | return i; |
740 | } |
741 | |
742 | O& operator[](size_t i) { |
743 | insert(i, O()); |
744 | return i < size ? *objects[i] : invalid; |
745 | } |
746 | |
747 | O* find(size_t i) const { return i < size ? objects[i] : nullptr; } |
748 | |
749 | template <typename T> |
750 | void on_erase(T*, ...) {} |
751 | template <typename T> |
752 | void on_erase(T* o, decltype(&T::on_erase)) { |
753 | o->on_erase(); |
754 | } |
755 | |
756 | bool erase(size_t i, bool should_delete = true) { |
757 | if (i < size && objects[i]) { |
758 | on_erase(objects[i], nullptr); |
759 | if (should_delete) { |
760 | delete objects[i]; |
761 | } |
762 | objects[i] = nullptr; |
763 | if (i < first_free) first_free = i; |
764 | return true; |
765 | } |
766 | return false; |
767 | } |
768 | |
769 | O** begin() const { return objects; } |
770 | O** end() const { return &objects[size]; } |
771 | }; |
772 | |
773 | struct Context { |
Excessive padding in 'struct Context' (38 padding bytes, where 6 is optimal). Optimal fields order: blendcolor, cleardepth, queries, framebuffers, renderbuffers, shaders, buffers, programs, textures, vertex_arrays, references, last_error, blendfunc_srgb, blendfunc_drgb, blendfunc_sa, blendfunc_da, blend_equation, depthfunc, unpack_row_length, shaded_rows, shaded_pixels, active_texture_unit, current_program, current_vertex_array, pixel_pack_buffer_binding, pixel_unpack_buffer_binding, array_buffer_binding, time_elapsed_query, samples_passed_query, renderbuffer_binding, draw_framebuffer_binding, read_framebuffer_binding, unknown_binding, viewport, scissor, clearcolor, texture_units, blend, blend_key, depthtest, depthmask, scissortest, validate_vertex_array, consider reordering the fields or adding explicit padding members | |
774 | int32_t references = 1; |
775 | |
776 | ObjectStore<Query> queries; |
777 | ObjectStore<Buffer> buffers; |
778 | ObjectStore<Texture> textures; |
779 | ObjectStore<VertexArray> vertex_arrays; |
780 | ObjectStore<Framebuffer> framebuffers; |
781 | ObjectStore<Renderbuffer> renderbuffers; |
782 | ObjectStore<Shader> shaders; |
783 | ObjectStore<Program> programs; |
784 | |
785 | GLenum last_error = GL_NO_ERROR0; |
786 | |
787 | IntRect viewport = {0, 0, 0, 0}; |
788 | |
789 | bool blend = false; |
790 | GLenum blendfunc_srgb = GL_ONE1; |
791 | GLenum blendfunc_drgb = GL_ZERO0; |
792 | GLenum blendfunc_sa = GL_ONE1; |
793 | GLenum blendfunc_da = GL_ZERO0; |
794 | GLenum blend_equation = GL_FUNC_ADD0x8006; |
795 | V8<uint16_t> blendcolor = 0; |
796 | BlendKey blend_key = BLEND_KEY_NONE; |
797 | |
798 | bool depthtest = false; |
799 | bool depthmask = true; |
800 | GLenum depthfunc = GL_LESS0x0201; |
801 | |
802 | bool scissortest = false; |
803 | IntRect scissor = {0, 0, 0, 0}; |
804 | |
805 | GLfloat clearcolor[4] = {0, 0, 0, 0}; |
806 | GLdouble cleardepth = 1; |
807 | |
808 | int unpack_row_length = 0; |
809 | |
810 | int shaded_rows = 0; |
811 | int shaded_pixels = 0; |
812 | |
813 | struct TextureUnit { |
814 | GLuint texture_2d_binding = 0; |
815 | GLuint texture_rectangle_binding = 0; |
816 | |
817 | void unlink(GLuint n) { |
818 | ::unlink(texture_2d_binding, n); |
819 | ::unlink(texture_rectangle_binding, n); |
820 | } |
821 | }; |
822 | TextureUnit texture_units[MAX_TEXTURE_UNITS]; |
823 | int active_texture_unit = 0; |
824 | |
825 | GLuint current_program = 0; |
826 | |
827 | GLuint current_vertex_array = 0; |
828 | bool validate_vertex_array = true; |
829 | |
830 | GLuint pixel_pack_buffer_binding = 0; |
831 | GLuint pixel_unpack_buffer_binding = 0; |
832 | GLuint array_buffer_binding = 0; |
833 | GLuint time_elapsed_query = 0; |
834 | GLuint samples_passed_query = 0; |
835 | GLuint renderbuffer_binding = 0; |
836 | GLuint draw_framebuffer_binding = 0; |
837 | GLuint read_framebuffer_binding = 0; |
838 | GLuint unknown_binding = 0; |
839 | |
840 | GLuint& get_binding(GLenum name) { |
841 | switch (name) { |
842 | case GL_PIXEL_PACK_BUFFER0x88EB: |
843 | return pixel_pack_buffer_binding; |
844 | case GL_PIXEL_UNPACK_BUFFER0x88EC: |
845 | return pixel_unpack_buffer_binding; |
846 | case GL_ARRAY_BUFFER0x8892: |
847 | return array_buffer_binding; |
848 | case GL_ELEMENT_ARRAY_BUFFER0x8893: |
849 | return vertex_arrays[current_vertex_array].element_array_buffer_binding; |
850 | case GL_TEXTURE_2D0x0DE1: |
851 | return texture_units[active_texture_unit].texture_2d_binding; |
852 | case GL_TEXTURE_RECTANGLE0x84F5: |
853 | return texture_units[active_texture_unit].texture_rectangle_binding; |
854 | case GL_TIME_ELAPSED0x88BF: |
855 | return time_elapsed_query; |
856 | case GL_SAMPLES_PASSED0x8914: |
857 | return samples_passed_query; |
858 | case GL_RENDERBUFFER0x8D41: |
859 | return renderbuffer_binding; |
860 | case GL_DRAW_FRAMEBUFFER0x8CA9: |
861 | return draw_framebuffer_binding; |
862 | case GL_READ_FRAMEBUFFER0x8CA8: |
863 | return read_framebuffer_binding; |
864 | default: |
865 | debugf("unknown binding %x\n", name)printf("unknown binding %x\n", name); |
866 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
867 | return unknown_binding; |
868 | } |
869 | } |
870 | |
871 | Texture& get_texture(sampler2D, int unit) { |
872 | return textures[texture_units[unit].texture_2d_binding]; |
873 | } |
874 | |
875 | Texture& get_texture(isampler2D, int unit) { |
876 | return textures[texture_units[unit].texture_2d_binding]; |
877 | } |
878 | |
879 | Texture& get_texture(sampler2DRect, int unit) { |
880 | return textures[texture_units[unit].texture_rectangle_binding]; |
881 | } |
882 | |
883 | IntRect apply_scissor(IntRect bb, |
884 | const IntPoint& origin = IntPoint(0, 0)) const { |
885 | return scissortest ? bb.intersect(scissor - origin) : bb; |
886 | } |
887 | |
888 | IntRect apply_scissor(const Texture& t) const { |
889 | return apply_scissor(t.bounds(), t.offset); |
890 | } |
891 | }; |
892 | static Context* ctx = nullptr; |
893 | static VertexShaderImpl* vertex_shader = nullptr; |
894 | static FragmentShaderImpl* fragment_shader = nullptr; |
895 | static BlendKey blend_key = BLEND_KEY_NONE; |
896 | |
897 | static void prepare_texture(Texture& t, const IntRect* skip = nullptr); |
898 | |
899 | template <typename S> |
900 | static inline void init_filter(S* s, Texture& t) { |
901 | // If the width is not at least 2 pixels, then we can't safely sample the end |
902 | // of the row with a linear filter. In that case, just punt to using nearest |
903 | // filtering instead. |
904 | s->filter = t.width >= 2 ? gl_filter_to_texture_filter(t.mag_filter) |
905 | : TextureFilter::NEAREST; |
906 | } |
907 | |
908 | template <typename S> |
909 | static inline void init_sampler(S* s, Texture& t) { |
910 | prepare_texture(t); |
911 | s->width = t.width; |
912 | s->height = t.height; |
913 | s->stride = t.stride(); |
914 | int bpp = t.bpp(); |
915 | if (bpp >= 4) |
916 | s->stride /= 4; |
917 | else if (bpp == 2) |
918 | s->stride /= 2; |
919 | else |
920 | assert(bpp == 1)(static_cast <bool> (bpp == 1) ? void (0) : __assert_fail ("bpp == 1", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
921 | // Use uint32_t* for easier sampling, but need to cast to uint8_t* or |
922 | // uint16_t* for formats with bpp < 4. |
923 | s->buf = (uint32_t*)t.buf; |
924 | s->format = gl_format_to_texture_format(t.internal_format); |
925 | } |
926 | |
927 | template <typename S> |
928 | static inline void null_sampler(S* s) { |
929 | // For null texture data, just make the sampler provide a 1x1 buffer that is |
930 | // transparent black. Ensure buffer holds at least a SIMD vector of zero data |
931 | // for SIMD padding of unaligned loads. |
932 | static const uint32_t zeroBuf[sizeof(Float) / sizeof(uint32_t)] = {0}; |
933 | s->width = 1; |
934 | s->height = 1; |
935 | s->stride = s->width; |
936 | s->buf = (uint32_t*)zeroBuf; |
937 | s->format = TextureFormat::RGBA8; |
938 | } |
939 | |
940 | template <typename S> |
941 | static inline void null_filter(S* s) { |
942 | s->filter = TextureFilter::NEAREST; |
943 | } |
944 | |
945 | template <typename S> |
946 | S* lookup_sampler(S* s, int texture) { |
947 | Texture& t = ctx->get_texture(s, texture); |
948 | if (!t.buf) { |
949 | null_sampler(s); |
950 | null_filter(s); |
951 | } else { |
952 | init_sampler(s, t); |
953 | init_filter(s, t); |
954 | } |
955 | return s; |
956 | } |
957 | |
958 | template <typename S> |
959 | S* lookup_isampler(S* s, int texture) { |
960 | Texture& t = ctx->get_texture(s, texture); |
961 | if (!t.buf) { |
962 | null_sampler(s); |
963 | } else { |
964 | init_sampler(s, t); |
965 | } |
966 | return s; |
967 | } |
968 | |
969 | int bytes_per_type(GLenum type) { |
970 | switch (type) { |
971 | case GL_INT0x1404: |
972 | return 4; |
973 | case GL_FLOAT0x1406: |
974 | return 4; |
975 | case GL_UNSIGNED_SHORT0x1403: |
976 | return 2; |
977 | case GL_UNSIGNED_BYTE0x1401: |
978 | return 1; |
979 | default: |
980 | assert(0)(static_cast <bool> (0) ? void (0) : __assert_fail ("0" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
981 | return 0; |
982 | } |
983 | } |
984 | |
985 | template <typename S, typename C> |
986 | static inline S expand_attrib(const char* buf, size_t size, bool normalized) { |
987 | typedef typename ElementType<S>::ty elem_type; |
988 | S scalar = {0}; |
989 | const C* src = reinterpret_cast<const C*>(buf); |
990 | if (normalized) { |
991 | const float scale = 1.0f / ((1 << (8 * sizeof(C))) - 1); |
992 | for (size_t i = 0; i < size / sizeof(C); i++) { |
993 | put_nth_component(scalar, i, elem_type(src[i]) * scale); |
994 | } |
995 | } else { |
996 | for (size_t i = 0; i < size / sizeof(C); i++) { |
997 | put_nth_component(scalar, i, elem_type(src[i])); |
998 | } |
999 | } |
1000 | return scalar; |
1001 | } |
1002 | |
1003 | template <typename S> |
1004 | static inline S load_attrib_scalar(VertexAttrib& va, const char* src) { |
1005 | if (sizeof(S) <= va.size) { |
1006 | return *reinterpret_cast<const S*>(src); |
1007 | } |
1008 | if (va.type == GL_UNSIGNED_SHORT0x1403) { |
1009 | return expand_attrib<S, uint16_t>(src, va.size, va.normalized); |
1010 | } |
1011 | if (va.type == GL_UNSIGNED_BYTE0x1401) { |
1012 | return expand_attrib<S, uint8_t>(src, va.size, va.normalized); |
1013 | } |
1014 | assert(sizeof(typename ElementType<S>::ty) == bytes_per_type(va.type))(static_cast <bool> (sizeof(typename ElementType<S> ::ty) == bytes_per_type(va.type)) ? void (0) : __assert_fail ( "sizeof(typename ElementType<S>::ty) == bytes_per_type(va.type)" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1015 | S scalar = {0}; |
1016 | memcpy(&scalar, src, va.size); |
1017 | return scalar; |
1018 | } |
1019 | |
1020 | template <typename T> |
1021 | void load_attrib(T& attrib, VertexAttrib& va, uint32_t start, int instance, |
1022 | int count) { |
1023 | typedef decltype(force_scalar(attrib)) scalar_type; |
1024 | // If no buffer is available, just use a zero default. |
1025 | if (!va.buf_size) { |
1026 | attrib = T(scalar_type{0}); |
1027 | } else if (va.divisor != 0) { |
1028 | char* src = (char*)va.buf + va.stride * instance + va.offset; |
1029 | assert(src + va.size <= va.buf + va.buf_size)(static_cast <bool> (src + va.size <= va.buf + va.buf_size ) ? void (0) : __assert_fail ("src + va.size <= va.buf + va.buf_size" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1030 | attrib = T(load_attrib_scalar<scalar_type>(va, src)); |
1031 | } else { |
1032 | // Specialized for WR's primitive vertex order/winding. |
1033 | if (!count) return; |
1034 | assert(count >= 2 && count <= 4)(static_cast <bool> (count >= 2 && count <= 4) ? void (0) : __assert_fail ("count >= 2 && count <= 4" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1035 | char* src = (char*)va.buf + va.stride * start + va.offset; |
1036 | switch (count) { |
1037 | case 2: { |
1038 | // Lines must be indexed at offsets 0, 1. |
1039 | // Line vertexes fill vertex shader SIMD lanes as 0, 1, 1, 0. |
1040 | scalar_type lanes[2] = { |
1041 | load_attrib_scalar<scalar_type>(va, src), |
1042 | load_attrib_scalar<scalar_type>(va, src + va.stride)}; |
1043 | attrib = (T){lanes[0], lanes[1], lanes[1], lanes[0]}; |
1044 | break; |
1045 | } |
1046 | case 3: { |
1047 | // Triangles must be indexed at offsets 0, 1, 2. |
1048 | // Triangle vertexes fill vertex shader SIMD lanes as 0, 1, 2, 2. |
1049 | scalar_type lanes[3] = { |
1050 | load_attrib_scalar<scalar_type>(va, src), |
1051 | load_attrib_scalar<scalar_type>(va, src + va.stride), |
1052 | load_attrib_scalar<scalar_type>(va, src + va.stride * 2)}; |
1053 | attrib = (T){lanes[0], lanes[1], lanes[2], lanes[2]}; |
1054 | break; |
1055 | } |
1056 | default: |
1057 | // Quads must be successive triangles indexed at offsets 0, 1, 2, 2, |
1058 | // 1, 3. Quad vertexes fill vertex shader SIMD lanes as 0, 1, 3, 2, so |
1059 | // that the points form a convex path that can be traversed by the |
1060 | // rasterizer. |
1061 | attrib = (T){load_attrib_scalar<scalar_type>(va, src), |
1062 | load_attrib_scalar<scalar_type>(va, src + va.stride), |
1063 | load_attrib_scalar<scalar_type>(va, src + va.stride * 3), |
1064 | load_attrib_scalar<scalar_type>(va, src + va.stride * 2)}; |
1065 | break; |
1066 | } |
1067 | } |
1068 | } |
1069 | |
1070 | template <typename T> |
1071 | void load_flat_attrib(T& attrib, VertexAttrib& va, uint32_t start, int instance, |
1072 | int count) { |
1073 | typedef decltype(force_scalar(attrib)) scalar_type; |
1074 | // If no buffer is available, just use a zero default. |
1075 | if (!va.buf_size) { |
1076 | attrib = T{0}; |
1077 | return; |
1078 | } |
1079 | char* src = nullptr; |
1080 | if (va.divisor != 0) { |
1081 | src = (char*)va.buf + va.stride * instance + va.offset; |
1082 | } else { |
1083 | if (!count) return; |
1084 | src = (char*)va.buf + va.stride * start + va.offset; |
1085 | } |
1086 | assert(src + va.size <= va.buf + va.buf_size)(static_cast <bool> (src + va.size <= va.buf + va.buf_size ) ? void (0) : __assert_fail ("src + va.size <= va.buf + va.buf_size" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1087 | attrib = T(load_attrib_scalar<scalar_type>(va, src)); |
1088 | } |
1089 | |
1090 | void setup_program(GLuint program) { |
1091 | if (!program) { |
1092 | vertex_shader = nullptr; |
1093 | fragment_shader = nullptr; |
1094 | return; |
1095 | } |
1096 | Program& p = ctx->programs[program]; |
1097 | assert(p.impl)(static_cast <bool> (p.impl) ? void (0) : __assert_fail ("p.impl", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
1098 | assert(p.vert_impl)(static_cast <bool> (p.vert_impl) ? void (0) : __assert_fail ("p.vert_impl", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
1099 | assert(p.frag_impl)(static_cast <bool> (p.frag_impl) ? void (0) : __assert_fail ("p.frag_impl", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
1100 | vertex_shader = p.vert_impl; |
1101 | fragment_shader = p.frag_impl; |
1102 | } |
1103 | |
1104 | extern ProgramLoader load_shader(const char* name); |
1105 | |
1106 | extern "C" { |
1107 | |
1108 | void UseProgram(GLuint program) { |
1109 | if (ctx->current_program && program != ctx->current_program) { |
1110 | auto* p = ctx->programs.find(ctx->current_program); |
1111 | if (p && p->deleted) { |
1112 | ctx->programs.erase(ctx->current_program); |
1113 | } |
1114 | } |
1115 | ctx->current_program = program; |
1116 | setup_program(program); |
1117 | } |
1118 | |
1119 | void SetViewport(GLint x, GLint y, GLsizei width, GLsizei height) { |
1120 | ctx->viewport = IntRect{x, y, x + width, y + height}; |
1121 | } |
1122 | |
1123 | void Enable(GLenum cap) { |
1124 | switch (cap) { |
1125 | case GL_BLEND0x0BE2: |
1126 | ctx->blend = true; |
1127 | break; |
1128 | case GL_DEPTH_TEST0x0B71: |
1129 | ctx->depthtest = true; |
1130 | break; |
1131 | case GL_SCISSOR_TEST0x0C11: |
1132 | ctx->scissortest = true; |
1133 | break; |
1134 | } |
1135 | } |
1136 | |
1137 | void Disable(GLenum cap) { |
1138 | switch (cap) { |
1139 | case GL_BLEND0x0BE2: |
1140 | ctx->blend = false; |
1141 | break; |
1142 | case GL_DEPTH_TEST0x0B71: |
1143 | ctx->depthtest = false; |
1144 | break; |
1145 | case GL_SCISSOR_TEST0x0C11: |
1146 | ctx->scissortest = false; |
1147 | break; |
1148 | } |
1149 | } |
1150 | |
1151 | // Report the last error generated and clear the error status. |
1152 | GLenum GetError() { |
1153 | GLenum error = ctx->last_error; |
1154 | ctx->last_error = GL_NO_ERROR0; |
1155 | return error; |
1156 | } |
1157 | |
1158 | // Sets the error status to out-of-memory to indicate that a buffer |
1159 | // or texture re-allocation failed. |
1160 | static void out_of_memory() { ctx->last_error = GL_OUT_OF_MEMORY0x0505; } |
1161 | |
1162 | static const char* const extensions[] = { |
1163 | "GL_ARB_blend_func_extended", |
1164 | "GL_ARB_clear_texture", |
1165 | "GL_ARB_copy_image", |
1166 | "GL_ARB_draw_instanced", |
1167 | "GL_ARB_explicit_attrib_location", |
1168 | "GL_ARB_instanced_arrays", |
1169 | "GL_ARB_invalidate_subdata", |
1170 | "GL_ARB_texture_storage", |
1171 | "GL_EXT_timer_query", |
1172 | "GL_KHR_blend_equation_advanced", |
1173 | "GL_KHR_blend_equation_advanced_coherent", |
1174 | "GL_APPLE_rgb_422", |
1175 | }; |
1176 | |
1177 | void GetIntegerv(GLenum pname, GLint* params) { |
1178 | assert(params)(static_cast <bool> (params) ? void (0) : __assert_fail ("params", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
1179 | switch (pname) { |
1180 | case GL_MAX_TEXTURE_UNITS0x84E2: |
1181 | case GL_MAX_TEXTURE_IMAGE_UNITS0x8872: |
1182 | params[0] = MAX_TEXTURE_UNITS; |
1183 | break; |
1184 | case GL_MAX_TEXTURE_SIZE0x0D33: |
1185 | params[0] = 1 << 15; |
1186 | break; |
1187 | case GL_MAX_ARRAY_TEXTURE_LAYERS0x88FF: |
1188 | params[0] = 0; |
1189 | break; |
1190 | case GL_READ_FRAMEBUFFER_BINDING0x8CAA: |
1191 | params[0] = ctx->read_framebuffer_binding; |
1192 | break; |
1193 | case GL_DRAW_FRAMEBUFFER_BINDING0x8CA6: |
1194 | params[0] = ctx->draw_framebuffer_binding; |
1195 | break; |
1196 | case GL_PIXEL_PACK_BUFFER_BINDING0x88ED: |
1197 | params[0] = ctx->pixel_pack_buffer_binding; |
1198 | break; |
1199 | case GL_PIXEL_UNPACK_BUFFER_BINDING0x88EF: |
1200 | params[0] = ctx->pixel_unpack_buffer_binding; |
1201 | break; |
1202 | case GL_NUM_EXTENSIONS0x821D: |
1203 | params[0] = sizeof(extensions) / sizeof(extensions[0]); |
1204 | break; |
1205 | case GL_MAJOR_VERSION0x821B: |
1206 | params[0] = 3; |
1207 | break; |
1208 | case GL_MINOR_VERSION0x821C: |
1209 | params[0] = 2; |
1210 | break; |
1211 | case GL_MIN_PROGRAM_TEXEL_OFFSET0x8904: |
1212 | params[0] = 0; |
1213 | break; |
1214 | case GL_MAX_PROGRAM_TEXEL_OFFSET0x8905: |
1215 | params[0] = MAX_TEXEL_OFFSET; |
1216 | break; |
1217 | default: |
1218 | debugf("unhandled glGetIntegerv parameter %x\n", pname)printf("unhandled glGetIntegerv parameter %x\n", pname); |
1219 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1220 | } |
1221 | } |
1222 | |
1223 | void GetBooleanv(GLenum pname, GLboolean* params) { |
1224 | assert(params)(static_cast <bool> (params) ? void (0) : __assert_fail ("params", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
1225 | switch (pname) { |
1226 | case GL_DEPTH_WRITEMASK0x0B72: |
1227 | params[0] = ctx->depthmask; |
1228 | break; |
1229 | default: |
1230 | debugf("unhandled glGetBooleanv parameter %x\n", pname)printf("unhandled glGetBooleanv parameter %x\n", pname); |
1231 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1232 | } |
1233 | } |
1234 | |
1235 | const char* GetString(GLenum name) { |
1236 | switch (name) { |
1237 | case GL_VENDOR0x1F00: |
1238 | return "Mozilla Gfx"; |
1239 | case GL_RENDERER0x1F01: |
1240 | return "Software WebRender"; |
1241 | case GL_VERSION0x1F02: |
1242 | return "3.2"; |
1243 | case GL_SHADING_LANGUAGE_VERSION0x8B8C: |
1244 | return "1.50"; |
1245 | default: |
1246 | debugf("unhandled glGetString parameter %x\n", name)printf("unhandled glGetString parameter %x\n", name); |
1247 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1248 | return nullptr; |
1249 | } |
1250 | } |
1251 | |
1252 | const char* GetStringi(GLenum name, GLuint index) { |
1253 | switch (name) { |
1254 | case GL_EXTENSIONS0x1F03: |
1255 | if (index >= sizeof(extensions) / sizeof(extensions[0])) { |
1256 | return nullptr; |
1257 | } |
1258 | return extensions[index]; |
1259 | default: |
1260 | debugf("unhandled glGetStringi parameter %x\n", name)printf("unhandled glGetStringi parameter %x\n", name); |
1261 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1262 | return nullptr; |
1263 | } |
1264 | } |
1265 | |
1266 | GLenum remap_blendfunc(GLenum rgb, GLenum a) { |
1267 | switch (a) { |
1268 | case GL_SRC_ALPHA0x0302: |
1269 | if (rgb == GL_SRC_COLOR0x0300) a = GL_SRC_COLOR0x0300; |
1270 | break; |
1271 | case GL_ONE_MINUS_SRC_ALPHA0x0303: |
1272 | if (rgb == GL_ONE_MINUS_SRC_COLOR0x0301) a = GL_ONE_MINUS_SRC_COLOR0x0301; |
1273 | break; |
1274 | case GL_DST_ALPHA0x0304: |
1275 | if (rgb == GL_DST_COLOR0x0306) a = GL_DST_COLOR0x0306; |
1276 | break; |
1277 | case GL_ONE_MINUS_DST_ALPHA0x0305: |
1278 | if (rgb == GL_ONE_MINUS_DST_COLOR0x0307) a = GL_ONE_MINUS_DST_COLOR0x0307; |
1279 | break; |
1280 | case GL_CONSTANT_ALPHA0x8003: |
1281 | if (rgb == GL_CONSTANT_COLOR0x8001) a = GL_CONSTANT_COLOR0x8001; |
1282 | break; |
1283 | case GL_ONE_MINUS_CONSTANT_ALPHA0x8004: |
1284 | if (rgb == GL_ONE_MINUS_CONSTANT_COLOR0x8002) a = GL_ONE_MINUS_CONSTANT_COLOR0x8002; |
1285 | break; |
1286 | case GL_SRC_COLOR0x0300: |
1287 | if (rgb == GL_SRC_ALPHA0x0302) a = GL_SRC_ALPHA0x0302; |
1288 | break; |
1289 | case GL_ONE_MINUS_SRC_COLOR0x0301: |
1290 | if (rgb == GL_ONE_MINUS_SRC_ALPHA0x0303) a = GL_ONE_MINUS_SRC_ALPHA0x0303; |
1291 | break; |
1292 | case GL_DST_COLOR0x0306: |
1293 | if (rgb == GL_DST_ALPHA0x0304) a = GL_DST_ALPHA0x0304; |
1294 | break; |
1295 | case GL_ONE_MINUS_DST_COLOR0x0307: |
1296 | if (rgb == GL_ONE_MINUS_DST_ALPHA0x0305) a = GL_ONE_MINUS_DST_ALPHA0x0305; |
1297 | break; |
1298 | case GL_CONSTANT_COLOR0x8001: |
1299 | if (rgb == GL_CONSTANT_ALPHA0x8003) a = GL_CONSTANT_ALPHA0x8003; |
1300 | break; |
1301 | case GL_ONE_MINUS_CONSTANT_COLOR0x8002: |
1302 | if (rgb == GL_ONE_MINUS_CONSTANT_ALPHA0x8004) a = GL_ONE_MINUS_CONSTANT_ALPHA0x8004; |
1303 | break; |
1304 | case GL_SRC1_ALPHA0x8589: |
1305 | if (rgb == GL_SRC1_COLOR0x88F9) a = GL_SRC1_COLOR0x88F9; |
1306 | break; |
1307 | case GL_ONE_MINUS_SRC1_ALPHA0x88FB: |
1308 | if (rgb == GL_ONE_MINUS_SRC1_COLOR0x88FA) a = GL_ONE_MINUS_SRC1_COLOR0x88FA; |
1309 | break; |
1310 | case GL_SRC1_COLOR0x88F9: |
1311 | if (rgb == GL_SRC1_ALPHA0x8589) a = GL_SRC1_ALPHA0x8589; |
1312 | break; |
1313 | case GL_ONE_MINUS_SRC1_COLOR0x88FA: |
1314 | if (rgb == GL_ONE_MINUS_SRC1_ALPHA0x88FB) a = GL_ONE_MINUS_SRC1_ALPHA0x88FB; |
1315 | break; |
1316 | } |
1317 | return a; |
1318 | } |
1319 | |
1320 | // Generate a hashed blend key based on blend func and equation state. This |
1321 | // allows all the blend state to be processed down to a blend key that can be |
1322 | // dealt with inside a single switch statement. |
1323 | static void hash_blend_key() { |
1324 | GLenum srgb = ctx->blendfunc_srgb; |
1325 | GLenum drgb = ctx->blendfunc_drgb; |
1326 | GLenum sa = ctx->blendfunc_sa; |
1327 | GLenum da = ctx->blendfunc_da; |
1328 | GLenum equation = ctx->blend_equation; |
1329 | #define HASH_BLEND_KEY(x, y, z, w)((x << 4) | (y) | (z << 24) | (w << 20)) ((x << 4) | (y) | (z << 24) | (w << 20)) |
1330 | // Basic non-separate blend funcs used the two argument form |
1331 | int hash = HASH_BLEND_KEY(srgb, drgb, 0, 0)((srgb << 4) | (drgb) | (0 << 24) | (0 << 20 )); |
1332 | // Separate alpha blend funcs use the 4 argument hash |
1333 | if (srgb != sa || drgb != da) hash |= HASH_BLEND_KEY(0, 0, sa, da)((0 << 4) | (0) | (sa << 24) | (da << 20)); |
1334 | // Any other blend equation than the default func_add ignores the func and |
1335 | // instead generates a one-argument hash based on the equation |
1336 | if (equation != GL_FUNC_ADD0x8006) hash = HASH_BLEND_KEY(equation, 0, 0, 0)((equation << 4) | (0) | (0 << 24) | (0 << 20 )); |
1337 | switch (hash) { |
1338 | #define MAP_BLEND_KEY(...)case HASH_BLEND_KEY: ctx->blend_key = BLEND_...000; break; \ |
1339 | case HASH_BLEND_KEY(__VA_ARGS__): \ |
1340 | ctx->blend_key = BLEND_KEY(__VA_ARGS__)BLEND___VA_ARGS__000; \ |
1341 | break; |
1342 | FOR_EACH_BLEND_KEY(MAP_BLEND_KEY)case ((1 << 4) | (0) | (0 << 24) | (0 << 20 )): ctx->blend_key = BLEND_1000; break; case ((0x0302 << 4) | (0x0303) | (1 << 24) | (0x0303 << 20)): ctx ->blend_key = BLEND_0x03020x030310x0303; break; case ((1 << 4) | (0x0303) | (0 << 24) | (0 << 20)): ctx-> blend_key = BLEND_10x030300; break; case ((0 << 4) | (0x0301 ) | (0 << 24) | (0 << 20)): ctx->blend_key = BLEND_00x030100 ; break; case ((0 << 4) | (0x0301) | (0 << 24) | ( 1 << 20)): ctx->blend_key = BLEND_00x030101; break; case ((0 << 4) | (0x0303) | (0 << 24) | (0 << 20 )): ctx->blend_key = BLEND_00x030300; break; case ((0 << 4) | (0x0300) | (0 << 24) | (0 << 20)): ctx-> blend_key = BLEND_00x030000; break; case ((1 << 4) | (1 ) | (0 << 24) | (0 << 20)): ctx->blend_key = BLEND_1100 ; break; case ((1 << 4) | (1) | (1 << 24) | (0x0303 << 20)): ctx->blend_key = BLEND_1110x0303; break; case ((0x0305 << 4) | (1) | (0 << 24) | (1 << 20 )): ctx->blend_key = BLEND_0x0305101; break; case ((0x8001 << 4) | (0x0301) | (0 << 24) | (0 << 20)): ctx->blend_key = BLEND_0x80010x030100; break; case ((1 << 4) | (0x88FA) | (0 << 24) | (0 << 20)): ctx-> blend_key = BLEND_10x88FA00; break; case ((0x8007 << 4) | (0) | (0 << 24) | (0 << 20)): ctx->blend_key = BLEND_0x8007000; break; case ((0x8008 << 4) | (0) | ( 0 << 24) | (0 << 20)): ctx->blend_key = BLEND_0x8008000 ; break; case ((0x9294 << 4) | (0) | (0 << 24) | ( 0 << 20)): ctx->blend_key = BLEND_0x9294000; break; case ((0x9295 << 4) | (0) | (0 << 24) | (0 << 20 )): ctx->blend_key = BLEND_0x9295000; break; case ((0x9296 << 4) | (0) | (0 << 24) | (0 << 20)): ctx-> blend_key = BLEND_0x9296000; break; case ((0x9297 << 4) | (0) | (0 << 24) | (0 << 20)): ctx->blend_key = BLEND_0x9297000; break; case ((0x9298 << 4) | (0) | ( 0 << 24) | (0 << 20)): ctx->blend_key = BLEND_0x9298000 ; break; case ((0x9299 << 4) | (0) | (0 << 24) | ( 0 << 20)): ctx->blend_key = BLEND_0x9299000; break; case ((0x929A << 4) | (0) | (0 << 24) | (0 << 20 )): ctx->blend_key = BLEND_0x929A000; break; case ((0x929B << 4) | (0) | (0 << 24) | (0 << 20)): ctx-> blend_key = BLEND_0x929B000; break; case ((0x929C << 4) | (0) | (0 << 24) | (0 << 20)): ctx->blend_key = BLEND_0x929C000; break; case ((0x929E << 4) | (0) | ( 0 << 24) | (0 << 20)): ctx->blend_key = BLEND_0x929E000 ; break; case ((0x92A0 << 4) | (0) | (0 << 24) | ( 0 << 20)): ctx->blend_key = BLEND_0x92A0000; break; case ((0x92AD << 4) | (0) | (0 << 24) | (0 << 20 )): ctx->blend_key = BLEND_0x92AD000; break; case ((0x92AE << 4) | (0) | (0 << 24) | (0 << 20)): ctx-> blend_key = BLEND_0x92AE000; break; case ((0x92AF << 4) | (0) | (0 << 24) | (0 << 20)): ctx->blend_key = BLEND_0x92AF000; break; case ((0x92B0 << 4) | (0) | ( 0 << 24) | (0 << 20)): ctx->blend_key = BLEND_0x92B0000 ; break; case ((0xB001 << 4) | (0) | (0 << 24) | ( 0 << 20)): ctx->blend_key = BLEND_0xB001000; break; case ((0xB002 << 4) | (0) | (0 << 24) | (0 << 20 )): ctx->blend_key = BLEND_0xB002000; break; |
1343 | default: |
1344 | debugf("blendfunc: %x, %x, separate: %x, %x, equation: %x\n", srgb, drgb,printf("blendfunc: %x, %x, separate: %x, %x, equation: %x\n", srgb, drgb, sa, da, equation) |
1345 | sa, da, equation)printf("blendfunc: %x, %x, separate: %x, %x, equation: %x\n", srgb, drgb, sa, da, equation); |
1346 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1347 | break; |
1348 | } |
1349 | } |
1350 | |
1351 | void BlendFunc(GLenum srgb, GLenum drgb, GLenum sa, GLenum da) { |
1352 | ctx->blendfunc_srgb = srgb; |
1353 | ctx->blendfunc_drgb = drgb; |
1354 | sa = remap_blendfunc(srgb, sa); |
1355 | da = remap_blendfunc(drgb, da); |
1356 | ctx->blendfunc_sa = sa; |
1357 | ctx->blendfunc_da = da; |
1358 | |
1359 | hash_blend_key(); |
1360 | } |
1361 | |
1362 | void BlendColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) { |
1363 | I32 c = round_pixel((Float){b, g, r, a}); |
1364 | ctx->blendcolor = CONVERT(c, U16)__builtin_convertvector(c, U16).xyzwxyzw; |
1365 | } |
1366 | |
1367 | void BlendEquation(GLenum mode) { |
1368 | assert(mode == GL_FUNC_ADD || mode == GL_MIN || mode == GL_MAX ||(static_cast <bool> (mode == 0x8006 || mode == 0x8007 || mode == 0x8008 || (mode >= 0x9294 && mode <= 0x92B0 )) ? void (0) : __assert_fail ("mode == GL_FUNC_ADD || mode == GL_MIN || mode == GL_MAX || (mode >= GL_MULTIPLY_KHR && mode <= GL_HSL_LUMINOSITY_KHR)" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )) |
1369 | (mode >= GL_MULTIPLY_KHR && mode <= GL_HSL_LUMINOSITY_KHR))(static_cast <bool> (mode == 0x8006 || mode == 0x8007 || mode == 0x8008 || (mode >= 0x9294 && mode <= 0x92B0 )) ? void (0) : __assert_fail ("mode == GL_FUNC_ADD || mode == GL_MIN || mode == GL_MAX || (mode >= GL_MULTIPLY_KHR && mode <= GL_HSL_LUMINOSITY_KHR)" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1370 | if (mode != ctx->blend_equation) { |
1371 | ctx->blend_equation = mode; |
1372 | hash_blend_key(); |
1373 | } |
1374 | } |
1375 | |
1376 | void DepthMask(GLboolean flag) { ctx->depthmask = flag; } |
1377 | |
1378 | void DepthFunc(GLenum func) { |
1379 | switch (func) { |
1380 | case GL_LESS0x0201: |
1381 | case GL_LEQUAL0x0203: |
1382 | break; |
1383 | default: |
1384 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1385 | } |
1386 | ctx->depthfunc = func; |
1387 | } |
1388 | |
1389 | void SetScissor(GLint x, GLint y, GLsizei width, GLsizei height) { |
1390 | ctx->scissor = IntRect{x, y, x + width, y + height}; |
1391 | } |
1392 | |
1393 | void ClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) { |
1394 | ctx->clearcolor[0] = r; |
1395 | ctx->clearcolor[1] = g; |
1396 | ctx->clearcolor[2] = b; |
1397 | ctx->clearcolor[3] = a; |
1398 | } |
1399 | |
1400 | void ClearDepth(GLdouble depth) { ctx->cleardepth = depth; } |
1401 | |
1402 | void ActiveTexture(GLenum texture) { |
1403 | assert(texture >= GL_TEXTURE0)(static_cast <bool> (texture >= 0x84C0) ? void (0) : __assert_fail ("texture >= GL_TEXTURE0", __builtin_FILE ( ), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
1404 | assert(texture < GL_TEXTURE0 + MAX_TEXTURE_UNITS)(static_cast <bool> (texture < 0x84C0 + MAX_TEXTURE_UNITS ) ? void (0) : __assert_fail ("texture < GL_TEXTURE0 + MAX_TEXTURE_UNITS" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1405 | ctx->active_texture_unit = |
1406 | clamp(int(texture - GL_TEXTURE00x84C0), 0, int(MAX_TEXTURE_UNITS - 1)); |
1407 | } |
1408 | |
1409 | void GenQueries(GLsizei n, GLuint* result) { |
1410 | for (int i = 0; i < n; i++) { |
1411 | Query q; |
1412 | result[i] = ctx->queries.insert(q); |
1413 | } |
1414 | } |
1415 | |
1416 | void DeleteQuery(GLuint n) { |
1417 | if (n && ctx->queries.erase(n)) { |
1418 | unlink(ctx->time_elapsed_query, n); |
1419 | unlink(ctx->samples_passed_query, n); |
1420 | } |
1421 | } |
1422 | |
1423 | void GenBuffers(int n, GLuint* result) { |
1424 | for (int i = 0; i < n; i++) { |
1425 | Buffer b; |
1426 | result[i] = ctx->buffers.insert(b); |
1427 | } |
1428 | } |
1429 | |
1430 | void DeleteBuffer(GLuint n) { |
1431 | if (n && ctx->buffers.erase(n)) { |
1432 | unlink(ctx->pixel_pack_buffer_binding, n); |
1433 | unlink(ctx->pixel_unpack_buffer_binding, n); |
1434 | unlink(ctx->array_buffer_binding, n); |
1435 | } |
1436 | } |
1437 | |
1438 | void GenVertexArrays(int n, GLuint* result) { |
1439 | for (int i = 0; i < n; i++) { |
1440 | VertexArray v; |
1441 | result[i] = ctx->vertex_arrays.insert(v); |
1442 | } |
1443 | } |
1444 | |
1445 | void DeleteVertexArray(GLuint n) { |
1446 | if (n && ctx->vertex_arrays.erase(n)) { |
1447 | unlink(ctx->current_vertex_array, n); |
1448 | } |
1449 | } |
1450 | |
1451 | GLuint CreateShader(GLenum type) { |
1452 | Shader s; |
1453 | s.type = type; |
1454 | return ctx->shaders.insert(s); |
1455 | } |
1456 | |
1457 | void ShaderSourceByName(GLuint shader, char* name) { |
1458 | Shader& s = ctx->shaders[shader]; |
1459 | s.loader = load_shader(name); |
1460 | if (!s.loader) { |
1461 | debugf("unknown shader %s\n", name)printf("unknown shader %s\n", name); |
1462 | } |
1463 | } |
1464 | |
1465 | void AttachShader(GLuint program, GLuint shader) { |
1466 | Program& p = ctx->programs[program]; |
1467 | Shader& s = ctx->shaders[shader]; |
1468 | if (s.type == GL_VERTEX_SHADER0x8B31) { |
1469 | if (!p.impl && s.loader) p.impl = s.loader(); |
1470 | } else if (s.type == GL_FRAGMENT_SHADER0x8B30) { |
1471 | if (!p.impl && s.loader) p.impl = s.loader(); |
1472 | } else { |
1473 | assert(0)(static_cast <bool> (0) ? void (0) : __assert_fail ("0" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1474 | } |
1475 | } |
1476 | |
1477 | void DeleteShader(GLuint n) { |
1478 | if (n) ctx->shaders.erase(n); |
1479 | } |
1480 | |
1481 | GLuint CreateProgram() { |
1482 | Program p; |
1483 | return ctx->programs.insert(p); |
1484 | } |
1485 | |
1486 | void DeleteProgram(GLuint n) { |
1487 | if (!n) return; |
1488 | if (ctx->current_program == n) { |
1489 | if (auto* p = ctx->programs.find(n)) { |
1490 | p->deleted = true; |
1491 | } |
1492 | } else { |
1493 | ctx->programs.erase(n); |
1494 | } |
1495 | } |
1496 | |
1497 | void LinkProgram(GLuint program) { |
1498 | Program& p = ctx->programs[program]; |
1499 | assert(p.impl)(static_cast <bool> (p.impl) ? void (0) : __assert_fail ("p.impl", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
1500 | if (!p.impl) { |
1501 | return; |
1502 | } |
1503 | assert(p.impl->interpolants_size() <= sizeof(Interpolants))(static_cast <bool> (p.impl->interpolants_size() <= sizeof(Interpolants)) ? void (0) : __assert_fail ("p.impl->interpolants_size() <= sizeof(Interpolants)" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1504 | if (!p.vert_impl) p.vert_impl = p.impl->get_vertex_shader(); |
1505 | if (!p.frag_impl) p.frag_impl = p.impl->get_fragment_shader(); |
1506 | } |
1507 | |
1508 | GLint GetLinkStatus(GLuint program) { |
1509 | if (auto* p = ctx->programs.find(program)) { |
1510 | return p->impl ? 1 : 0; |
1511 | } |
1512 | return 0; |
1513 | } |
1514 | |
1515 | void BindAttribLocation(GLuint program, GLuint index, char* name) { |
1516 | Program& p = ctx->programs[program]; |
1517 | assert(p.impl)(static_cast <bool> (p.impl) ? void (0) : __assert_fail ("p.impl", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
1518 | if (!p.impl) { |
1519 | return; |
1520 | } |
1521 | p.impl->bind_attrib(name, index); |
1522 | } |
1523 | |
1524 | GLint GetAttribLocation(GLuint program, char* name) { |
1525 | Program& p = ctx->programs[program]; |
1526 | assert(p.impl)(static_cast <bool> (p.impl) ? void (0) : __assert_fail ("p.impl", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
1527 | if (!p.impl) { |
1528 | return -1; |
1529 | } |
1530 | return p.impl->get_attrib(name); |
1531 | } |
1532 | |
1533 | GLint GetUniformLocation(GLuint program, char* name) { |
1534 | Program& p = ctx->programs[program]; |
1535 | assert(p.impl)(static_cast <bool> (p.impl) ? void (0) : __assert_fail ("p.impl", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
1536 | if (!p.impl) { |
1537 | return -1; |
1538 | } |
1539 | GLint loc = p.impl->get_uniform(name); |
1540 | // debugf("location: %d\n", loc); |
1541 | return loc; |
1542 | } |
1543 | |
1544 | static uint64_t get_time_value() { |
1545 | #ifdef __MACH__ |
1546 | return mach_absolute_time(); |
1547 | #elif defined(_WIN32) |
1548 | LARGE_INTEGER time; |
1549 | static bool have_frequency = false; |
1550 | static LARGE_INTEGER frequency; |
1551 | if (!have_frequency) { |
1552 | QueryPerformanceFrequency(&frequency); |
1553 | have_frequency = true; |
1554 | } |
1555 | QueryPerformanceCounter(&time); |
1556 | return time.QuadPart * 1000000000ULL / frequency.QuadPart; |
1557 | #else |
1558 | return ({ |
1559 | struct timespec tp; |
1560 | clock_gettime(CLOCK_MONOTONIC1, &tp); |
1561 | tp.tv_sec * 1000000000ULL + tp.tv_nsec; |
1562 | }); |
1563 | #endif |
1564 | } |
1565 | |
1566 | void BeginQuery(GLenum target, GLuint id) { |
1567 | ctx->get_binding(target) = id; |
1568 | Query& q = ctx->queries[id]; |
1569 | switch (target) { |
1570 | case GL_SAMPLES_PASSED0x8914: |
1571 | q.value = 0; |
1572 | break; |
1573 | case GL_TIME_ELAPSED0x88BF: |
1574 | q.value = get_time_value(); |
1575 | break; |
1576 | default: |
1577 | debugf("unknown query target %x for query %d\n", target, id)printf("unknown query target %x for query %d\n", target, id); |
1578 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1579 | } |
1580 | } |
1581 | |
1582 | void EndQuery(GLenum target) { |
1583 | Query& q = ctx->queries[ctx->get_binding(target)]; |
1584 | switch (target) { |
1585 | case GL_SAMPLES_PASSED0x8914: |
1586 | break; |
1587 | case GL_TIME_ELAPSED0x88BF: |
1588 | q.value = get_time_value() - q.value; |
1589 | break; |
1590 | default: |
1591 | debugf("unknown query target %x\n", target)printf("unknown query target %x\n", target); |
1592 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1593 | } |
1594 | ctx->get_binding(target) = 0; |
1595 | } |
1596 | |
1597 | void GetQueryObjectui64v(GLuint id, GLenum pname, GLuint64* params) { |
1598 | Query& q = ctx->queries[id]; |
1599 | switch (pname) { |
1600 | case GL_QUERY_RESULT0x8866: |
1601 | assert(params)(static_cast <bool> (params) ? void (0) : __assert_fail ("params", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
1602 | params[0] = q.value; |
1603 | break; |
1604 | default: |
1605 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1606 | } |
1607 | } |
1608 | |
1609 | void BindVertexArray(GLuint vertex_array) { |
1610 | if (vertex_array != ctx->current_vertex_array) { |
1611 | ctx->validate_vertex_array = true; |
1612 | } |
1613 | ctx->current_vertex_array = vertex_array; |
1614 | } |
1615 | |
1616 | void BindTexture(GLenum target, GLuint texture) { |
1617 | ctx->get_binding(target) = texture; |
1618 | } |
1619 | |
1620 | void BindBuffer(GLenum target, GLuint buffer) { |
1621 | ctx->get_binding(target) = buffer; |
1622 | } |
1623 | |
1624 | void BindFramebuffer(GLenum target, GLuint fb) { |
1625 | if (target == GL_FRAMEBUFFER0x8D40) { |
1626 | ctx->read_framebuffer_binding = fb; |
1627 | ctx->draw_framebuffer_binding = fb; |
1628 | } else { |
1629 | assert(target == GL_READ_FRAMEBUFFER || target == GL_DRAW_FRAMEBUFFER)(static_cast <bool> (target == 0x8CA8 || target == 0x8CA9 ) ? void (0) : __assert_fail ("target == GL_READ_FRAMEBUFFER || target == GL_DRAW_FRAMEBUFFER" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1630 | ctx->get_binding(target) = fb; |
1631 | } |
1632 | } |
1633 | |
1634 | void BindRenderbuffer(GLenum target, GLuint rb) { |
1635 | ctx->get_binding(target) = rb; |
1636 | } |
1637 | |
1638 | void PixelStorei(GLenum name, GLint param) { |
1639 | if (name == GL_UNPACK_ALIGNMENT0x0CF5) { |
1640 | assert(param == 1)(static_cast <bool> (param == 1) ? void (0) : __assert_fail ("param == 1", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
1641 | } else if (name == GL_UNPACK_ROW_LENGTH0x0CF2) { |
1642 | ctx->unpack_row_length = param; |
1643 | } |
1644 | } |
1645 | |
1646 | static GLenum remap_internal_format(GLenum format) { |
1647 | switch (format) { |
1648 | case GL_DEPTH_COMPONENT0x1902: |
1649 | return GL_DEPTH_COMPONENT240x81A6; |
1650 | case GL_RGBA0x1908: |
1651 | return GL_RGBA80x8058; |
1652 | case GL_RED0x1903: |
1653 | return GL_R80x8229; |
1654 | case GL_RG0x8227: |
1655 | return GL_RG80x822B; |
1656 | case GL_RGB_422_APPLE0x8A1F: |
1657 | return GL_RGB_RAW_422_APPLE0x8A51; |
1658 | default: |
1659 | return format; |
1660 | } |
1661 | } |
1662 | |
1663 | } // extern "C" |
1664 | |
1665 | static bool format_requires_conversion(GLenum external_format, |
1666 | GLenum internal_format) { |
1667 | switch (external_format) { |
1668 | case GL_RGBA0x1908: |
1669 | return internal_format == GL_RGBA80x8058; |
1670 | case GL_RED0x1903: |
1671 | return internal_format != GL_R80x8229 && internal_format != GL_R160x822A; |
1672 | case GL_RG0x8227: |
1673 | return internal_format != GL_RG80x822B && internal_format != GL_RG160x822C; |
1674 | default: |
1675 | return false; |
1676 | } |
1677 | } |
1678 | |
1679 | static inline void copy_bgra8_to_rgba8(uint32_t* dest, const uint32_t* src, |
1680 | int width) { |
1681 | for (; width >= 4; width -= 4, dest += 4, src += 4) { |
1682 | U32 p = unaligned_load<U32>(src); |
1683 | U32 rb = p & 0x00FF00FF; |
1684 | unaligned_store(dest, (p & 0xFF00FF00) | (rb << 16) | (rb >> 16)); |
1685 | } |
1686 | for (; width > 0; width--, dest++, src++) { |
1687 | uint32_t p = *src; |
1688 | uint32_t rb = p & 0x00FF00FF; |
1689 | *dest = (p & 0xFF00FF00) | (rb << 16) | (rb >> 16); |
1690 | } |
1691 | } |
1692 | |
1693 | static inline void copy_red_to_rgba32f(float* dest, const float* src, |
1694 | int width) { |
1695 | for (; width > 0; width--, dest += 4, src++) { |
1696 | dest[0] = *src; |
1697 | dest[1] = 0.0f; |
1698 | dest[2] = 0.0f; |
1699 | dest[3] = 1.0f; |
1700 | } |
1701 | } |
1702 | |
1703 | static inline void copy_red_to_bgra8(uint8_t* dest, const uint8_t* src, |
1704 | int width) { |
1705 | for (; width > 0; width--, dest += 4, src++) { |
1706 | dest[0] = 0; |
1707 | dest[1] = 0; |
1708 | dest[2] = *src; |
1709 | dest[3] = 255; |
1710 | } |
1711 | } |
1712 | |
1713 | template <typename T, size_t N = 1> |
1714 | static int clip_ptrs_against_bounds(T*& dst_buf, T* dst_bound0, T* dst_bound1, |
1715 | const T*& src_buf, const T* src_bound0, |
1716 | const T* src_bound1, size_t& len) { |
1717 | if (dst_bound0) { |
1718 | assert(dst_bound0 <= dst_bound1)(static_cast <bool> (dst_bound0 <= dst_bound1) ? void (0) : __assert_fail ("dst_bound0 <= dst_bound1", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
1719 | if (dst_buf < dst_bound0) { |
1720 | size_t offset = size_t(dst_bound0 - dst_buf) / N; |
1721 | if (len <= offset) { |
1722 | // dst entirely before bounds |
1723 | len = 0; |
1724 | return -1; |
1725 | } |
1726 | // dst overlaps bound0 |
1727 | src_buf += offset; |
1728 | dst_buf += offset * N; |
1729 | len -= offset; |
1730 | } |
1731 | if (dst_buf >= dst_bound1) { |
1732 | // dst entirely after bounds |
1733 | len = 0; |
1734 | return 1; |
1735 | } |
1736 | size_t remaining = size_t(dst_bound1 - dst_buf) / N; |
1737 | if (len > remaining) { |
1738 | // dst overlaps bound1 |
1739 | len = remaining; |
1740 | } |
1741 | } |
1742 | if (src_bound0) { |
1743 | assert(src_bound0 <= src_bound1)(static_cast <bool> (src_bound0 <= src_bound1) ? void (0) : __assert_fail ("src_bound0 <= src_bound1", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
1744 | if (src_buf < src_bound0) { |
1745 | size_t offset = size_t(src_bound0 - src_buf); |
1746 | if (len <= offset) { |
1747 | // src entirely before bounds |
1748 | len = 0; |
1749 | return -1; |
1750 | } |
1751 | // src overlaps bound0 |
1752 | src_buf += offset; |
1753 | dst_buf += offset * N; |
1754 | len -= offset; |
1755 | } |
1756 | if (src_buf >= src_bound1) { |
1757 | // src entirely after bounds |
1758 | len = 0; |
1759 | return 1; |
1760 | } |
1761 | size_t remaining = size_t(src_bound1 - src_buf); |
1762 | if (len > remaining) { |
1763 | // src overlaps bound1 |
1764 | len = remaining; |
1765 | } |
1766 | } |
1767 | return 0; |
1768 | } |
1769 | |
1770 | static void convert_copy(GLenum external_format, GLenum internal_format, |
1771 | uint8_t* dst_buf, size_t dst_stride, |
1772 | uint8_t* dst_bound0, uint8_t* dst_bound1, |
1773 | const uint8_t* src_buf, size_t src_stride, |
1774 | const uint8_t* src_bound0, const uint8_t* src_bound1, |
1775 | size_t width, size_t height) { |
1776 | switch (external_format) { |
1777 | case GL_RGBA0x1908: |
1778 | if (internal_format == GL_RGBA80x8058) { |
1779 | for (; height; height--) { |
1780 | size_t len = width; |
1781 | uint32_t* dst_ptr = (uint32_t*)dst_buf; |
1782 | const uint32_t* src_ptr = (const uint32_t*)src_buf; |
1783 | if (clip_ptrs_against_bounds(dst_ptr, (uint32_t*)dst_bound0, |
1784 | (uint32_t*)dst_bound1, src_ptr, |
1785 | (const uint32_t*)src_bound0, |
1786 | (const uint32_t*)src_bound1, len) > 0) { |
1787 | return; |
1788 | } |
1789 | if (len) { |
1790 | copy_bgra8_to_rgba8(dst_ptr, src_ptr, len); |
1791 | } |
1792 | dst_buf += dst_stride; |
1793 | src_buf += src_stride; |
1794 | } |
1795 | return; |
1796 | } |
1797 | break; |
1798 | case GL_RED0x1903: |
1799 | switch (internal_format) { |
1800 | case GL_RGBA80x8058: |
1801 | for (; height; height--) { |
1802 | size_t len = width; |
1803 | uint8_t* dst_ptr = dst_buf; |
1804 | const uint8_t* src_ptr = src_buf; |
1805 | if (clip_ptrs_against_bounds<uint8_t, 4>( |
1806 | dst_ptr, dst_bound0, dst_bound1, src_ptr, src_bound0, |
1807 | src_bound1, len) > 0) { |
1808 | return; |
1809 | } |
1810 | if (len) { |
1811 | copy_red_to_bgra8(dst_ptr, src_ptr, len); |
1812 | } |
1813 | dst_buf += dst_stride; |
1814 | src_buf += src_stride; |
1815 | } |
1816 | return; |
1817 | case GL_RGBA32F0x8814: |
1818 | for (; height; height--) { |
1819 | size_t len = width; |
1820 | float* dst_ptr = (float*)dst_buf; |
1821 | const float* src_ptr = (const float*)src_buf; |
1822 | if (clip_ptrs_against_bounds<float, 4>( |
1823 | dst_ptr, (float*)dst_bound0, (float*)dst_bound1, src_ptr, |
1824 | (const float*)src_bound0, (const float*)src_bound1, |
1825 | len) > 0) { |
1826 | return; |
1827 | } |
1828 | if (len) { |
1829 | copy_red_to_rgba32f(dst_ptr, src_ptr, len); |
1830 | } |
1831 | dst_buf += dst_stride; |
1832 | src_buf += src_stride; |
1833 | } |
1834 | return; |
1835 | case GL_R80x8229: |
1836 | break; |
1837 | default: |
1838 | debugf("unsupported format conversion from %x to %x\n",printf("unsupported format conversion from %x to %x\n", external_format , internal_format) |
1839 | external_format, internal_format)printf("unsupported format conversion from %x to %x\n", external_format , internal_format); |
1840 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1841 | return; |
1842 | } |
1843 | break; |
1844 | default: |
1845 | break; |
1846 | } |
1847 | size_t row_bytes = width * bytes_for_internal_format(internal_format); |
1848 | for (; height; height--) { |
1849 | size_t len = row_bytes; |
1850 | uint8_t* dst_ptr = dst_buf; |
1851 | const uint8_t* src_ptr = src_buf; |
1852 | if (clip_ptrs_against_bounds(dst_ptr, dst_bound0, dst_bound1, src_ptr, |
1853 | src_bound0, src_bound1, len) > 0) { |
1854 | return; |
1855 | } |
1856 | if (len) { |
1857 | memcpy(dst_ptr, src_ptr, len); |
1858 | } |
1859 | dst_buf += dst_stride; |
1860 | src_buf += src_stride; |
1861 | } |
1862 | } |
1863 | |
1864 | static void set_tex_storage(Texture& t, GLenum external_format, GLsizei width, |
1865 | GLsizei height, void* buf = nullptr, |
1866 | GLsizei stride = 0, GLsizei min_width = 0, |
1867 | GLsizei min_height = 0) { |
1868 | GLenum internal_format = remap_internal_format(external_format); |
1869 | bool changed = false; |
1870 | if (t.width != width || t.height != height || |
1871 | t.internal_format != internal_format) { |
1872 | changed = true; |
1873 | t.internal_format = internal_format; |
1874 | t.width = width; |
1875 | t.height = height; |
1876 | } |
1877 | // If we are changed from an internally managed buffer to an externally |
1878 | // supplied one or vice versa, ensure that we clean up old buffer state. |
1879 | // However, if we have to convert the data from a non-native format, then |
1880 | // always treat it as internally managed since we will need to copy to an |
1881 | // internally managed native format buffer. |
1882 | bool should_free = buf == nullptr || format_requires_conversion( |
1883 | external_format, internal_format); |
1884 | if (t.should_free() != should_free) { |
1885 | changed = true; |
1886 | t.cleanup(); |
1887 | t.set_should_free(should_free); |
1888 | } |
1889 | // If now an external buffer, explicitly set it... |
1890 | if (!should_free) { |
1891 | t.set_buffer(buf, stride); |
1892 | } |
1893 | t.disable_delayed_clear(); |
1894 | if (!t.allocate(changed, min_width, min_height)) { |
1895 | out_of_memory(); |
1896 | } |
1897 | // If we have a buffer that needs format conversion, then do that now. |
1898 | if (buf && should_free) { |
1899 | convert_copy(external_format, internal_format, (uint8_t*)t.buf, t.stride(), |
1900 | (uint8_t*)t.buf, (uint8_t*)t.end_ptr(), (const uint8_t*)buf, |
1901 | stride, nullptr, nullptr, width, height); |
1902 | } |
1903 | } |
1904 | |
1905 | extern "C" { |
1906 | |
1907 | void TexStorage2D(GLenum target, GLint levels, GLenum internal_format, |
1908 | GLsizei width, GLsizei height) { |
1909 | assert(levels == 1)(static_cast <bool> (levels == 1) ? void (0) : __assert_fail ("levels == 1", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
1910 | Texture& t = ctx->textures[ctx->get_binding(target)]; |
1911 | set_tex_storage(t, internal_format, width, height); |
1912 | } |
1913 | |
1914 | GLenum internal_format_for_data(GLenum format, GLenum ty) { |
1915 | if (format == GL_RED0x1903 && ty == GL_UNSIGNED_BYTE0x1401) { |
1916 | return GL_R80x8229; |
1917 | } else if ((format == GL_RGBA0x1908 || format == GL_BGRA0x80E1) && |
1918 | (ty == GL_UNSIGNED_BYTE0x1401 || ty == GL_UNSIGNED_INT_8_8_8_8_REV0x8367)) { |
1919 | return GL_RGBA80x8058; |
1920 | } else if (format == GL_RGBA0x1908 && ty == GL_FLOAT0x1406) { |
1921 | return GL_RGBA32F0x8814; |
1922 | } else if (format == GL_RGBA_INTEGER0x8D99 && ty == GL_INT0x1404) { |
1923 | return GL_RGBA32I0x8D82; |
1924 | } else if (format == GL_RG0x8227 && ty == GL_UNSIGNED_BYTE0x1401) { |
1925 | return GL_RG80x822B; |
1926 | } else if (format == GL_RGB_422_APPLE0x8A1F && |
1927 | ty == GL_UNSIGNED_SHORT_8_8_REV_APPLE0x85BB) { |
1928 | return GL_RGB_RAW_422_APPLE0x8A51; |
1929 | } else if (format == GL_RED0x1903 && ty == GL_UNSIGNED_SHORT0x1403) { |
1930 | return GL_R160x822A; |
1931 | } else if (format == GL_RG0x8227 && ty == GL_UNSIGNED_SHORT0x1403) { |
1932 | return GL_RG160x822C; |
1933 | } else { |
1934 | debugf("unknown internal format for format %x, type %x\n", format, ty)printf("unknown internal format for format %x, type %x\n", format , ty); |
1935 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1936 | return 0; |
1937 | } |
1938 | } |
1939 | |
1940 | static Buffer* get_pixel_pack_buffer() { |
1941 | return ctx->pixel_pack_buffer_binding |
1942 | ? &ctx->buffers[ctx->pixel_pack_buffer_binding] |
1943 | : nullptr; |
1944 | } |
1945 | |
1946 | static Buffer* get_pixel_unpack_buffer() { |
1947 | return ctx->pixel_unpack_buffer_binding |
1948 | ? &ctx->buffers[ctx->pixel_unpack_buffer_binding] |
1949 | : nullptr; |
1950 | } |
1951 | |
1952 | void TexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, |
1953 | GLsizei width, GLsizei height, GLenum format, GLenum ty, |
1954 | void* data) { |
1955 | if (level != 0) { |
1956 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1957 | return; |
1958 | } |
1959 | Buffer* pbo = get_pixel_unpack_buffer(); |
1960 | if (pbo) { |
1961 | data = pbo->get_data(data); |
1962 | } |
1963 | if (!data) return; |
1964 | Texture& t = ctx->textures[ctx->get_binding(target)]; |
1965 | IntRect skip = {xoffset, yoffset, xoffset + width, yoffset + height}; |
1966 | prepare_texture(t, &skip); |
1967 | assert(xoffset + width <= t.width)(static_cast <bool> (xoffset + width <= t.width) ? void (0) : __assert_fail ("xoffset + width <= t.width", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
1968 | assert(yoffset + height <= t.height)(static_cast <bool> (yoffset + height <= t.height) ? void (0) : __assert_fail ("yoffset + height <= t.height", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1969 | assert(ctx->unpack_row_length == 0 || ctx->unpack_row_length >= width)(static_cast <bool> (ctx->unpack_row_length == 0 || ctx ->unpack_row_length >= width) ? void (0) : __assert_fail ("ctx->unpack_row_length == 0 || ctx->unpack_row_length >= width" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1970 | GLsizei row_length = |
1971 | ctx->unpack_row_length != 0 ? ctx->unpack_row_length : width; |
1972 | assert(t.internal_format == internal_format_for_data(format, ty))(static_cast <bool> (t.internal_format == internal_format_for_data (format, ty)) ? void (0) : __assert_fail ("t.internal_format == internal_format_for_data(format, ty)" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1973 | int src_bpp = format_requires_conversion(format, t.internal_format) |
1974 | ? bytes_for_internal_format(format) |
1975 | : t.bpp(); |
1976 | if (!src_bpp || !t.buf) return; |
1977 | convert_copy(format, t.internal_format, |
1978 | (uint8_t*)t.sample_ptr(xoffset, yoffset), t.stride(), |
1979 | (uint8_t*)t.buf, (uint8_t*)t.end_ptr(), (const uint8_t*)data, |
1980 | row_length * src_bpp, pbo ? (const uint8_t*)pbo->buf : nullptr, |
1981 | pbo ? (const uint8_t*)pbo->end_ptr() : nullptr, width, height); |
1982 | } |
1983 | |
1984 | void TexImage2D(GLenum target, GLint level, GLint internal_format, |
1985 | GLsizei width, GLsizei height, GLint border, GLenum format, |
1986 | GLenum ty, void* data) { |
1987 | if (level != 0) { |
1988 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
1989 | return; |
1990 | } |
1991 | assert(border == 0)(static_cast <bool> (border == 0) ? void (0) : __assert_fail ("border == 0", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
1992 | TexStorage2D(target, 1, internal_format, width, height); |
1993 | TexSubImage2D(target, 0, 0, 0, width, height, format, ty, data); |
1994 | } |
1995 | |
1996 | void GenerateMipmap(UNUSED[[maybe_unused]] GLenum target) { |
1997 | // TODO: support mipmaps |
1998 | } |
1999 | |
2000 | void SetTextureParameter(GLuint texid, GLenum pname, GLint param) { |
2001 | Texture& t = ctx->textures[texid]; |
2002 | switch (pname) { |
2003 | case GL_TEXTURE_WRAP_S0x2802: |
2004 | assert(param == GL_CLAMP_TO_EDGE)(static_cast <bool> (param == 0x812F) ? void (0) : __assert_fail ("param == GL_CLAMP_TO_EDGE", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
2005 | break; |
2006 | case GL_TEXTURE_WRAP_T0x2803: |
2007 | assert(param == GL_CLAMP_TO_EDGE)(static_cast <bool> (param == 0x812F) ? void (0) : __assert_fail ("param == GL_CLAMP_TO_EDGE", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
2008 | break; |
2009 | case GL_TEXTURE_MIN_FILTER0x2801: |
2010 | t.min_filter = param; |
2011 | break; |
2012 | case GL_TEXTURE_MAG_FILTER0x2800: |
2013 | t.mag_filter = param; |
2014 | break; |
2015 | default: |
2016 | break; |
2017 | } |
2018 | } |
2019 | |
2020 | void TexParameteri(GLenum target, GLenum pname, GLint param) { |
2021 | SetTextureParameter(ctx->get_binding(target), pname, param); |
2022 | } |
2023 | |
2024 | typedef Texture LockedTexture; |
2025 | |
2026 | // Lock the given texture to prevent modification. |
2027 | LockedTexture* LockTexture(GLuint texId) { |
2028 | Texture& tex = ctx->textures[texId]; |
2029 | if (!tex.buf) { |
2030 | assert(tex.buf != nullptr)(static_cast <bool> (tex.buf != nullptr) ? void (0) : __assert_fail ("tex.buf != nullptr", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
2031 | return nullptr; |
2032 | } |
2033 | if (__sync_fetch_and_add(&tex.locked, 1) == 0) { |
2034 | // If this is the first time locking the texture, flush any delayed clears. |
2035 | prepare_texture(tex); |
2036 | } |
2037 | return (LockedTexture*)&tex; |
2038 | } |
2039 | |
2040 | // Lock the given framebuffer's color attachment to prevent modification. |
2041 | LockedTexture* LockFramebuffer(GLuint fboId) { |
2042 | Framebuffer& fb = ctx->framebuffers[fboId]; |
2043 | // Only allow locking a framebuffer if it has a valid color attachment. |
2044 | if (!fb.color_attachment) { |
2045 | assert(fb.color_attachment != 0)(static_cast <bool> (fb.color_attachment != 0) ? void ( 0) : __assert_fail ("fb.color_attachment != 0", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
2046 | return nullptr; |
2047 | } |
2048 | return LockTexture(fb.color_attachment); |
2049 | } |
2050 | |
2051 | // Reference an already locked resource |
2052 | void LockResource(LockedTexture* resource) { |
2053 | if (!resource) { |
2054 | return; |
2055 | } |
2056 | __sync_fetch_and_add(&resource->locked, 1); |
2057 | } |
2058 | |
2059 | // Remove a lock on a texture that has been previously locked |
2060 | int32_t UnlockResource(LockedTexture* resource) { |
2061 | if (!resource) { |
2062 | return -1; |
2063 | } |
2064 | int32_t locked = __sync_fetch_and_add(&resource->locked, -1); |
2065 | if (locked <= 0) { |
2066 | // The lock should always be non-zero before unlocking. |
2067 | assert(0)(static_cast <bool> (0) ? void (0) : __assert_fail ("0" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2068 | } else if (locked == 1 && resource->zombie()) { |
2069 | // If the resource is being kept alive by locks and this is the last lock, |
2070 | // then delete the resource now. |
2071 | delete resource; |
2072 | } |
2073 | return locked - 1; |
2074 | } |
2075 | |
2076 | void GenTextures(int n, GLuint* result) { |
2077 | for (int i = 0; i < n; i++) { |
2078 | Texture t; |
2079 | result[i] = ctx->textures.insert(t); |
2080 | } |
2081 | } |
2082 | |
2083 | void DeleteTexture(GLuint n) { |
2084 | if (!n) { |
2085 | return; |
2086 | } |
2087 | LockedTexture* tex = (LockedTexture*)ctx->textures.find(n); |
2088 | if (!tex) { |
2089 | return; |
2090 | } |
2091 | // Lock the texture so that it can't be deleted by another thread yet. |
2092 | LockResource(tex); |
2093 | // Forget the existing binding to the texture but keep it alive in case there |
2094 | // are any other locks on it. |
2095 | if (ctx->textures.erase(n, false)) { |
2096 | for (size_t i = 0; i < MAX_TEXTURE_UNITS; i++) { |
2097 | ctx->texture_units[i].unlink(n); |
2098 | } |
2099 | } |
2100 | // Mark the texture as a zombie so that it will be freed if there are no other |
2101 | // existing locks on it. |
2102 | tex->set_zombie(true); |
2103 | if (int32_t locked = UnlockResource(tex)) { |
2104 | debugf("DeleteTexture(%u) with %d locks\n", n, locked)printf("DeleteTexture(%u) with %d locks\n", n, locked); |
2105 | } |
2106 | } |
2107 | |
2108 | void GenRenderbuffers(int n, GLuint* result) { |
2109 | for (int i = 0; i < n; i++) { |
2110 | Renderbuffer r; |
2111 | result[i] = ctx->renderbuffers.insert(r); |
2112 | } |
2113 | } |
2114 | |
2115 | void Renderbuffer::on_erase() { |
2116 | for (auto* fb : ctx->framebuffers) { |
2117 | if (fb) { |
2118 | unlink(fb->color_attachment, texture); |
2119 | unlink(fb->depth_attachment, texture); |
2120 | } |
2121 | } |
2122 | DeleteTexture(texture); |
2123 | } |
2124 | |
2125 | void DeleteRenderbuffer(GLuint n) { |
2126 | if (n && ctx->renderbuffers.erase(n)) { |
2127 | unlink(ctx->renderbuffer_binding, n); |
2128 | } |
2129 | } |
2130 | |
2131 | void GenFramebuffers(int n, GLuint* result) { |
2132 | for (int i = 0; i < n; i++) { |
2133 | Framebuffer f; |
2134 | result[i] = ctx->framebuffers.insert(f); |
2135 | } |
2136 | } |
2137 | |
2138 | void DeleteFramebuffer(GLuint n) { |
2139 | if (n && ctx->framebuffers.erase(n)) { |
2140 | unlink(ctx->read_framebuffer_binding, n); |
2141 | unlink(ctx->draw_framebuffer_binding, n); |
2142 | } |
2143 | } |
2144 | |
2145 | void RenderbufferStorage(GLenum target, GLenum internal_format, GLsizei width, |
2146 | GLsizei height) { |
2147 | // Just refer a renderbuffer to a texture to simplify things for now... |
2148 | Renderbuffer& r = ctx->renderbuffers[ctx->get_binding(target)]; |
2149 | if (!r.texture) { |
2150 | GenTextures(1, &r.texture); |
2151 | } |
2152 | switch (internal_format) { |
2153 | case GL_DEPTH_COMPONENT0x1902: |
2154 | case GL_DEPTH_COMPONENT160x81A5: |
2155 | case GL_DEPTH_COMPONENT240x81A6: |
2156 | case GL_DEPTH_COMPONENT320x81A7: |
2157 | // Force depth format to 24 bits... |
2158 | internal_format = GL_DEPTH_COMPONENT240x81A6; |
2159 | break; |
2160 | } |
2161 | set_tex_storage(ctx->textures[r.texture], internal_format, width, height); |
2162 | } |
2163 | |
2164 | void VertexAttribPointer(GLuint index, GLint size, GLenum type, bool normalized, |
2165 | GLsizei stride, GLuint offset) { |
2166 | // debugf("cva: %d\n", ctx->current_vertex_array); |
2167 | VertexArray& v = ctx->vertex_arrays[ctx->current_vertex_array]; |
2168 | if (index >= NULL_ATTRIB16) { |
2169 | assert(0)(static_cast <bool> (0) ? void (0) : __assert_fail ("0" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2170 | return; |
2171 | } |
2172 | VertexAttrib& va = v.attribs[index]; |
2173 | va.size = size * bytes_per_type(type); |
2174 | va.type = type; |
2175 | va.normalized = normalized; |
2176 | va.stride = stride; |
2177 | va.offset = offset; |
2178 | // Buffer &vertex_buf = ctx->buffers[ctx->array_buffer_binding]; |
2179 | va.vertex_buffer = ctx->array_buffer_binding; |
2180 | va.vertex_array = ctx->current_vertex_array; |
2181 | ctx->validate_vertex_array = true; |
2182 | } |
2183 | |
2184 | void VertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, |
2185 | GLuint offset) { |
2186 | // debugf("cva: %d\n", ctx->current_vertex_array); |
2187 | VertexArray& v = ctx->vertex_arrays[ctx->current_vertex_array]; |
2188 | if (index >= NULL_ATTRIB16) { |
2189 | assert(0)(static_cast <bool> (0) ? void (0) : __assert_fail ("0" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2190 | return; |
2191 | } |
2192 | VertexAttrib& va = v.attribs[index]; |
2193 | va.size = size * bytes_per_type(type); |
2194 | va.type = type; |
2195 | va.normalized = false; |
2196 | va.stride = stride; |
2197 | va.offset = offset; |
2198 | // Buffer &vertex_buf = ctx->buffers[ctx->array_buffer_binding]; |
2199 | va.vertex_buffer = ctx->array_buffer_binding; |
2200 | va.vertex_array = ctx->current_vertex_array; |
2201 | ctx->validate_vertex_array = true; |
2202 | } |
2203 | |
2204 | void EnableVertexAttribArray(GLuint index) { |
2205 | VertexArray& v = ctx->vertex_arrays[ctx->current_vertex_array]; |
2206 | if (index >= NULL_ATTRIB16) { |
2207 | assert(0)(static_cast <bool> (0) ? void (0) : __assert_fail ("0" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2208 | return; |
2209 | } |
2210 | VertexAttrib& va = v.attribs[index]; |
2211 | if (!va.enabled) { |
2212 | ctx->validate_vertex_array = true; |
2213 | } |
2214 | va.enabled = true; |
2215 | v.max_attrib = max(v.max_attrib, (int)index); |
2216 | } |
2217 | |
2218 | void DisableVertexAttribArray(GLuint index) { |
2219 | VertexArray& v = ctx->vertex_arrays[ctx->current_vertex_array]; |
2220 | if (index >= NULL_ATTRIB16) { |
2221 | assert(0)(static_cast <bool> (0) ? void (0) : __assert_fail ("0" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2222 | return; |
2223 | } |
2224 | VertexAttrib& va = v.attribs[index]; |
2225 | if (va.enabled) { |
2226 | ctx->validate_vertex_array = true; |
2227 | } |
2228 | va.disable(); |
2229 | } |
2230 | |
2231 | void VertexAttribDivisor(GLuint index, GLuint divisor) { |
2232 | VertexArray& v = ctx->vertex_arrays[ctx->current_vertex_array]; |
2233 | // Only support divisor being 0 (per-vertex) or 1 (per-instance). |
2234 | if (index >= NULL_ATTRIB16 || divisor > 1) { |
2235 | assert(0)(static_cast <bool> (0) ? void (0) : __assert_fail ("0" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2236 | return; |
2237 | } |
2238 | VertexAttrib& va = v.attribs[index]; |
2239 | va.divisor = divisor; |
2240 | } |
2241 | |
2242 | void BufferData(GLenum target, GLsizeiptr size, void* data, |
2243 | UNUSED[[maybe_unused]] GLenum usage) { |
2244 | if (size < 0) { |
2245 | assert(0)(static_cast <bool> (0) ? void (0) : __assert_fail ("0" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2246 | return; |
2247 | } |
2248 | Buffer& b = ctx->buffers[ctx->get_binding(target)]; |
2249 | if (size != b.size) { |
2250 | if (!b.allocate(size)) { |
2251 | out_of_memory(); |
2252 | } |
2253 | ctx->validate_vertex_array = true; |
2254 | } |
2255 | if (data && b.buf && size <= b.size) { |
2256 | memcpy(b.buf, data, size); |
2257 | } |
2258 | } |
2259 | |
2260 | void BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, |
2261 | void* data) { |
2262 | if (offset < 0 || size < 0) { |
2263 | assert(0)(static_cast <bool> (0) ? void (0) : __assert_fail ("0" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2264 | return; |
2265 | } |
2266 | Buffer& b = ctx->buffers[ctx->get_binding(target)]; |
2267 | assert(offset < b.size && size <= b.size - offset)(static_cast <bool> (offset < b.size && size <= b.size - offset) ? void (0) : __assert_fail ("offset < b.size && size <= b.size - offset" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2268 | if (data && b.buf && offset < b.size && size <= b.size - offset) { |
2269 | memcpy(&b.buf[offset], data, size); |
2270 | } |
2271 | } |
2272 | |
2273 | void* MapBuffer(GLenum target, UNUSED[[maybe_unused]] GLbitfield access) { |
2274 | Buffer& b = ctx->buffers[ctx->get_binding(target)]; |
2275 | return b.buf; |
2276 | } |
2277 | |
2278 | void* MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, |
2279 | UNUSED[[maybe_unused]] GLbitfield access) { |
2280 | Buffer& b = ctx->buffers[ctx->get_binding(target)]; |
2281 | if (b.buf && offset >= 0 && length > 0 && offset < b.size && |
2282 | length <= b.size - offset) { |
2283 | return b.buf + offset; |
2284 | } |
2285 | return nullptr; |
2286 | } |
2287 | |
2288 | GLboolean UnmapBuffer(GLenum target) { |
2289 | Buffer& b = ctx->buffers[ctx->get_binding(target)]; |
2290 | return b.buf != nullptr; |
2291 | } |
2292 | |
2293 | void Uniform1i(GLint location, GLint V0) { |
2294 | // debugf("tex: %d\n", (int)ctx->textures.size); |
2295 | if (vertex_shader) { |
2296 | vertex_shader->set_uniform_1i(location, V0); |
2297 | } |
2298 | } |
2299 | void Uniform4fv(GLint location, GLsizei count, const GLfloat* v) { |
2300 | assert(count == 1)(static_cast <bool> (count == 1) ? void (0) : __assert_fail ("count == 1", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
2301 | if (vertex_shader) { |
2302 | vertex_shader->set_uniform_4fv(location, v); |
2303 | } |
2304 | } |
2305 | void UniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, |
2306 | const GLfloat* value) { |
2307 | assert(count == 1)(static_cast <bool> (count == 1) ? void (0) : __assert_fail ("count == 1", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
2308 | assert(!transpose)(static_cast <bool> (!transpose) ? void (0) : __assert_fail ("!transpose", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
2309 | if (vertex_shader) { |
2310 | vertex_shader->set_uniform_matrix4fv(location, value); |
2311 | } |
2312 | } |
2313 | |
2314 | void FramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, |
2315 | GLuint texture, GLint level) { |
2316 | assert(target == GL_READ_FRAMEBUFFER || target == GL_DRAW_FRAMEBUFFER)(static_cast <bool> (target == 0x8CA8 || target == 0x8CA9 ) ? void (0) : __assert_fail ("target == GL_READ_FRAMEBUFFER || target == GL_DRAW_FRAMEBUFFER" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2317 | assert(textarget == GL_TEXTURE_2D || textarget == GL_TEXTURE_RECTANGLE)(static_cast <bool> (textarget == 0x0DE1 || textarget == 0x84F5) ? void (0) : __assert_fail ("textarget == GL_TEXTURE_2D || textarget == GL_TEXTURE_RECTANGLE" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2318 | assert(level == 0)(static_cast <bool> (level == 0) ? void (0) : __assert_fail ("level == 0", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
2319 | Framebuffer& fb = ctx->framebuffers[ctx->get_binding(target)]; |
2320 | if (attachment == GL_COLOR_ATTACHMENT00x8CE0) { |
2321 | fb.color_attachment = texture; |
2322 | } else if (attachment == GL_DEPTH_ATTACHMENT0x8D00) { |
2323 | fb.depth_attachment = texture; |
2324 | } else { |
2325 | assert(0)(static_cast <bool> (0) ? void (0) : __assert_fail ("0" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2326 | } |
2327 | } |
2328 | |
2329 | void FramebufferRenderbuffer(GLenum target, GLenum attachment, |
2330 | GLenum renderbuffertarget, GLuint renderbuffer) { |
2331 | assert(target == GL_READ_FRAMEBUFFER || target == GL_DRAW_FRAMEBUFFER)(static_cast <bool> (target == 0x8CA8 || target == 0x8CA9 ) ? void (0) : __assert_fail ("target == GL_READ_FRAMEBUFFER || target == GL_DRAW_FRAMEBUFFER" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2332 | assert(renderbuffertarget == GL_RENDERBUFFER)(static_cast <bool> (renderbuffertarget == 0x8D41) ? void (0) : __assert_fail ("renderbuffertarget == GL_RENDERBUFFER" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2333 | Framebuffer& fb = ctx->framebuffers[ctx->get_binding(target)]; |
2334 | Renderbuffer& rb = ctx->renderbuffers[renderbuffer]; |
2335 | if (attachment == GL_COLOR_ATTACHMENT00x8CE0) { |
2336 | fb.color_attachment = rb.texture; |
2337 | } else if (attachment == GL_DEPTH_ATTACHMENT0x8D00) { |
2338 | fb.depth_attachment = rb.texture; |
2339 | } else { |
2340 | assert(0)(static_cast <bool> (0) ? void (0) : __assert_fail ("0" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2341 | } |
2342 | } |
2343 | |
2344 | } // extern "C" |
2345 | |
2346 | static inline Framebuffer* get_framebuffer(GLenum target, |
2347 | bool fallback = false) { |
2348 | if (target == GL_FRAMEBUFFER0x8D40) { |
2349 | target = GL_DRAW_FRAMEBUFFER0x8CA9; |
2350 | } |
2351 | Framebuffer* fb = ctx->framebuffers.find(ctx->get_binding(target)); |
2352 | if (fallback && !fb) { |
2353 | // If the specified framebuffer isn't found and a fallback is requested, |
2354 | // use the default framebuffer. |
2355 | fb = &ctx->framebuffers[0]; |
2356 | } |
2357 | return fb; |
2358 | } |
2359 | |
2360 | template <typename T> |
2361 | static inline void fill_n(T* dst, size_t n, T val) { |
2362 | for (T* end = &dst[n]; dst < end; dst++) *dst = val; |
2363 | } |
2364 | |
2365 | #if USE_SSE21 |
2366 | template <> |
2367 | inline void fill_n<uint32_t>(uint32_t* dst, size_t n, uint32_t val) { |
2368 | __asm__ __volatile__("rep stosl\n" |
2369 | : "+D"(dst), "+c"(n) |
2370 | : "a"(val) |
2371 | : "memory", "cc"); |
2372 | } |
2373 | #endif |
2374 | |
2375 | static inline uint32_t clear_chunk(uint8_t value) { |
2376 | return uint32_t(value) * 0x01010101U; |
2377 | } |
2378 | |
2379 | static inline uint32_t clear_chunk(uint16_t value) { |
2380 | return uint32_t(value) | (uint32_t(value) << 16); |
2381 | } |
2382 | |
2383 | static inline uint32_t clear_chunk(uint32_t value) { return value; } |
2384 | |
2385 | template <typename T> |
2386 | static inline void clear_row(T* buf, size_t len, T value, uint32_t chunk) { |
2387 | const size_t N = sizeof(uint32_t) / sizeof(T); |
2388 | // fill any leading unaligned values |
2389 | if (N > 1) { |
2390 | size_t align = (-(intptr_t)buf & (sizeof(uint32_t) - 1)) / sizeof(T); |
2391 | if (align <= len) { |
2392 | fill_n(buf, align, value); |
2393 | len -= align; |
2394 | buf += align; |
2395 | } |
2396 | } |
2397 | // fill as many aligned chunks as possible |
2398 | fill_n((uint32_t*)buf, len / N, chunk); |
2399 | // fill any remaining values |
2400 | if (N > 1) { |
2401 | fill_n(buf + (len & ~(N - 1)), len & (N - 1), value); |
2402 | } |
2403 | } |
2404 | |
2405 | template <typename T> |
2406 | static void clear_buffer(Texture& t, T value, IntRect bb, int skip_start = 0, |
2407 | int skip_end = 0) { |
2408 | if (!t.buf) return; |
2409 | skip_start = max(skip_start, bb.x0); |
2410 | skip_end = max(skip_end, skip_start); |
2411 | assert(sizeof(T) == t.bpp())(static_cast <bool> (sizeof(T) == t.bpp()) ? void (0) : __assert_fail ("sizeof(T) == t.bpp()", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
2412 | size_t stride = t.stride(); |
2413 | // When clearing multiple full-width rows, collapse them into a single large |
2414 | // "row" to avoid redundant setup from clearing each row individually. Note |
2415 | // that we can only safely do this if the stride is tightly packed. |
2416 | if (bb.width() == t.width && bb.height() > 1 && skip_start >= skip_end && |
2417 | (t.should_free() || stride == t.width * sizeof(T))) { |
2418 | bb.x1 += (stride / sizeof(T)) * (bb.height() - 1); |
2419 | bb.y1 = bb.y0 + 1; |
2420 | } |
2421 | T* buf = (T*)t.sample_ptr(bb.x0, bb.y0); |
2422 | uint32_t chunk = clear_chunk(value); |
2423 | for (int rows = bb.height(); rows > 0; rows--) { |
2424 | if (bb.x0 < skip_start) { |
2425 | clear_row(buf, skip_start - bb.x0, value, chunk); |
2426 | } |
2427 | if (skip_end < bb.x1) { |
2428 | clear_row(buf + (skip_end - bb.x0), bb.x1 - skip_end, value, chunk); |
2429 | } |
2430 | buf += stride / sizeof(T); |
2431 | } |
2432 | } |
2433 | |
2434 | template <typename T> |
2435 | static inline void force_clear_row(Texture& t, int y, int skip_start = 0, |
2436 | int skip_end = 0) { |
2437 | assert(t.buf != nullptr)(static_cast <bool> (t.buf != nullptr) ? void (0) : __assert_fail ("t.buf != nullptr", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
2438 | assert(sizeof(T) == t.bpp())(static_cast <bool> (sizeof(T) == t.bpp()) ? void (0) : __assert_fail ("sizeof(T) == t.bpp()", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
2439 | assert(skip_start <= skip_end)(static_cast <bool> (skip_start <= skip_end) ? void ( 0) : __assert_fail ("skip_start <= skip_end", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
2440 | T* buf = (T*)t.sample_ptr(0, y); |
2441 | uint32_t chunk = clear_chunk((T)t.clear_val); |
2442 | if (skip_start > 0) { |
2443 | clear_row<T>(buf, skip_start, t.clear_val, chunk); |
2444 | } |
2445 | if (skip_end < t.width) { |
2446 | clear_row<T>(buf + skip_end, t.width - skip_end, t.clear_val, chunk); |
2447 | } |
2448 | } |
2449 | |
2450 | template <typename T> |
2451 | static void force_clear(Texture& t, const IntRect* skip = nullptr) { |
2452 | if (!t.delay_clear || !t.cleared_rows) { |
2453 | return; |
2454 | } |
2455 | int y0 = 0; |
2456 | int y1 = t.height; |
2457 | int skip_start = 0; |
2458 | int skip_end = 0; |
2459 | if (skip) { |
2460 | y0 = clamp(skip->y0, 0, t.height); |
2461 | y1 = clamp(skip->y1, y0, t.height); |
2462 | skip_start = clamp(skip->x0, 0, t.width); |
2463 | skip_end = clamp(skip->x1, skip_start, t.width); |
2464 | if (skip_start <= 0 && skip_end >= t.width && y0 <= 0 && y1 >= t.height) { |
2465 | t.disable_delayed_clear(); |
2466 | return; |
2467 | } |
2468 | } |
2469 | int num_masks = (y1 + 31) / 32; |
2470 | uint32_t* rows = t.cleared_rows; |
2471 | for (int i = y0 / 32; i < num_masks; i++) { |
2472 | uint32_t mask = rows[i]; |
2473 | if (mask != ~0U) { |
2474 | rows[i] = ~0U; |
2475 | int start = i * 32; |
2476 | while (mask) { |
2477 | int count = __builtin_ctz(mask); |
2478 | if (count > 0) { |
2479 | clear_buffer<T>(t, t.clear_val, |
2480 | IntRect{0, start, t.width, start + count}, skip_start, |
2481 | skip_end); |
2482 | t.delay_clear -= count; |
2483 | start += count; |
2484 | mask >>= count; |
2485 | } |
2486 | count = __builtin_ctz(mask + 1); |
2487 | start += count; |
2488 | mask >>= count; |
2489 | } |
2490 | int count = (i + 1) * 32 - start; |
2491 | if (count > 0) { |
2492 | clear_buffer<T>(t, t.clear_val, |
2493 | IntRect{0, start, t.width, start + count}, skip_start, |
2494 | skip_end); |
2495 | t.delay_clear -= count; |
2496 | } |
2497 | } |
2498 | } |
2499 | if (t.delay_clear <= 0) t.disable_delayed_clear(); |
2500 | } |
2501 | |
2502 | static void prepare_texture(Texture& t, const IntRect* skip) { |
2503 | if (t.delay_clear) { |
2504 | switch (t.internal_format) { |
2505 | case GL_RGBA80x8058: |
2506 | force_clear<uint32_t>(t, skip); |
2507 | break; |
2508 | case GL_R80x8229: |
2509 | force_clear<uint8_t>(t, skip); |
2510 | break; |
2511 | case GL_RG80x822B: |
2512 | force_clear<uint16_t>(t, skip); |
2513 | break; |
2514 | default: |
2515 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2516 | break; |
2517 | } |
2518 | } |
2519 | } |
2520 | |
2521 | // Setup a clear on a texture. This may either force an immediate clear or |
2522 | // potentially punt to a delayed clear, if applicable. |
2523 | template <typename T> |
2524 | static void request_clear(Texture& t, T value, const IntRect& scissor) { |
2525 | // If the clear would require a scissor, force clear anything outside |
2526 | // the scissor, and then immediately clear anything inside the scissor. |
2527 | if (!scissor.contains(t.offset_bounds())) { |
2528 | IntRect skip = scissor - t.offset; |
2529 | force_clear<T>(t, &skip); |
2530 | clear_buffer<T>(t, value, skip.intersection(t.bounds())); |
2531 | } else { |
2532 | // Do delayed clear for 2D texture without scissor. |
2533 | t.enable_delayed_clear(value); |
2534 | } |
2535 | } |
2536 | |
2537 | template <typename T> |
2538 | static inline void request_clear(Texture& t, T value) { |
2539 | // If scissoring is enabled, use the scissor rect. Otherwise, just scissor to |
2540 | // the entire texture bounds. |
2541 | request_clear(t, value, ctx->scissortest ? ctx->scissor : t.offset_bounds()); |
2542 | } |
2543 | |
2544 | extern "C" { |
2545 | |
2546 | void InitDefaultFramebuffer(int x, int y, int width, int height, int stride, |
2547 | void* buf) { |
2548 | Framebuffer& fb = ctx->framebuffers[0]; |
2549 | if (!fb.color_attachment) { |
2550 | GenTextures(1, &fb.color_attachment); |
2551 | } |
2552 | // If the dimensions or buffer properties changed, we need to reallocate |
2553 | // the underlying storage for the color buffer texture. |
2554 | Texture& colortex = ctx->textures[fb.color_attachment]; |
2555 | set_tex_storage(colortex, GL_RGBA80x8058, width, height, buf, stride); |
2556 | colortex.offset = IntPoint(x, y); |
2557 | if (!fb.depth_attachment) { |
2558 | GenTextures(1, &fb.depth_attachment); |
2559 | } |
2560 | // Ensure dimensions of the depth buffer match the color buffer. |
2561 | Texture& depthtex = ctx->textures[fb.depth_attachment]; |
2562 | set_tex_storage(depthtex, GL_DEPTH_COMPONENT240x81A6, width, height); |
2563 | depthtex.offset = IntPoint(x, y); |
2564 | } |
2565 | |
2566 | void* GetColorBuffer(GLuint fbo, GLboolean flush, int32_t* width, |
2567 | int32_t* height, int32_t* stride) { |
2568 | Framebuffer* fb = ctx->framebuffers.find(fbo); |
2569 | if (!fb || !fb->color_attachment) { |
2570 | return nullptr; |
2571 | } |
2572 | Texture& colortex = ctx->textures[fb->color_attachment]; |
2573 | if (flush) { |
2574 | prepare_texture(colortex); |
2575 | } |
2576 | assert(colortex.offset == IntPoint(0, 0))(static_cast <bool> (colortex.offset == IntPoint(0, 0)) ? void (0) : __assert_fail ("colortex.offset == IntPoint(0, 0)" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2577 | if (width) { |
2578 | *width = colortex.width; |
2579 | } |
2580 | if (height) { |
2581 | *height = colortex.height; |
2582 | } |
2583 | if (stride) { |
2584 | *stride = colortex.stride(); |
2585 | } |
2586 | return colortex.buf ? colortex.sample_ptr(0, 0) : nullptr; |
2587 | } |
2588 | |
2589 | void ResolveFramebuffer(GLuint fbo) { |
2590 | Framebuffer* fb = ctx->framebuffers.find(fbo); |
2591 | if (!fb || !fb->color_attachment) { |
2592 | return; |
2593 | } |
2594 | Texture& colortex = ctx->textures[fb->color_attachment]; |
2595 | prepare_texture(colortex); |
2596 | } |
2597 | |
2598 | void SetTextureBuffer(GLuint texid, GLenum internal_format, GLsizei width, |
2599 | GLsizei height, GLsizei stride, void* buf, |
2600 | GLsizei min_width, GLsizei min_height) { |
2601 | Texture& t = ctx->textures[texid]; |
2602 | set_tex_storage(t, internal_format, width, height, buf, stride, min_width, |
2603 | min_height); |
2604 | } |
2605 | |
2606 | GLenum CheckFramebufferStatus(GLenum target) { |
2607 | Framebuffer* fb = get_framebuffer(target); |
2608 | if (!fb || !fb->color_attachment) { |
2609 | return GL_FRAMEBUFFER_UNSUPPORTED0x8CDD; |
2610 | } |
2611 | return GL_FRAMEBUFFER_COMPLETE0x8CD5; |
2612 | } |
2613 | |
2614 | void ClearTexSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset, |
2615 | GLint zoffset, GLsizei width, GLsizei height, |
2616 | GLsizei depth, GLenum format, GLenum type, |
2617 | const void* data) { |
2618 | if (level != 0) { |
2619 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2620 | return; |
2621 | } |
2622 | Texture& t = ctx->textures[texture]; |
2623 | assert(!t.locked)(static_cast <bool> (!t.locked) ? void (0) : __assert_fail ("!t.locked", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
2624 | if (width <= 0 || height <= 0 || depth <= 0) { |
2625 | return; |
2626 | } |
2627 | assert(zoffset == 0 && depth == 1)(static_cast <bool> (zoffset == 0 && depth == 1 ) ? void (0) : __assert_fail ("zoffset == 0 && depth == 1" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2628 | IntRect scissor = {xoffset, yoffset, xoffset + width, yoffset + height}; |
2629 | if (t.internal_format == GL_DEPTH_COMPONENT240x81A6) { |
2630 | uint32_t value = 0xFFFFFF; |
2631 | switch (format) { |
2632 | case GL_DEPTH_COMPONENT0x1902: |
2633 | switch (type) { |
2634 | case GL_DOUBLE0x1408: |
2635 | value = uint32_t(*(const GLdouble*)data * 0xFFFFFF); |
2636 | break; |
2637 | case GL_FLOAT0x1406: |
2638 | value = uint32_t(*(const GLfloat*)data * 0xFFFFFF); |
2639 | break; |
2640 | default: |
2641 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2642 | break; |
2643 | } |
2644 | break; |
2645 | default: |
2646 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2647 | break; |
2648 | } |
2649 | if (t.cleared() && !scissor.contains(t.offset_bounds())) { |
2650 | // If we need to scissor the clear and the depth buffer was already |
2651 | // initialized, then just fill runs for that scissor area. |
2652 | t.fill_depth_runs(value, scissor); |
2653 | } else { |
2654 | // Otherwise, the buffer is either uninitialized or the clear would |
2655 | // encompass the entire buffer. If uninitialized, we can safely fill |
2656 | // the entire buffer with any value and thus ignore any scissoring. |
2657 | t.init_depth_runs(value); |
2658 | } |
2659 | return; |
2660 | } |
2661 | |
2662 | uint32_t color = 0xFF000000; |
2663 | switch (type) { |
2664 | case GL_FLOAT0x1406: { |
2665 | const GLfloat* f = (const GLfloat*)data; |
2666 | Float v = {0.0f, 0.0f, 0.0f, 1.0f}; |
2667 | switch (format) { |
2668 | case GL_RGBA0x1908: |
2669 | v.w = f[3]; // alpha |
2670 | FALLTHROUGH[[fallthrough]]; |
2671 | case GL_RGB0x1907: |
2672 | v.z = f[2]; // blue |
2673 | FALLTHROUGH[[fallthrough]]; |
2674 | case GL_RG0x8227: |
2675 | v.y = f[1]; // green |
2676 | FALLTHROUGH[[fallthrough]]; |
2677 | case GL_RED0x1903: |
2678 | v.x = f[0]; // red |
2679 | break; |
2680 | default: |
2681 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2682 | break; |
2683 | } |
2684 | color = bit_cast<uint32_t>(CONVERT(round_pixel(v), U8)__builtin_convertvector(round_pixel(v), U8)); |
2685 | break; |
2686 | } |
2687 | case GL_UNSIGNED_BYTE0x1401: { |
2688 | const GLubyte* b = (const GLubyte*)data; |
2689 | switch (format) { |
2690 | case GL_RGBA0x1908: |
2691 | color = (color & ~0xFF000000) | (uint32_t(b[3]) << 24); // alpha |
2692 | FALLTHROUGH[[fallthrough]]; |
2693 | case GL_RGB0x1907: |
2694 | color = (color & ~0x00FF0000) | (uint32_t(b[2]) << 16); // blue |
2695 | FALLTHROUGH[[fallthrough]]; |
2696 | case GL_RG0x8227: |
2697 | color = (color & ~0x0000FF00) | (uint32_t(b[1]) << 8); // green |
2698 | FALLTHROUGH[[fallthrough]]; |
2699 | case GL_RED0x1903: |
2700 | color = (color & ~0x000000FF) | uint32_t(b[0]); // red |
2701 | break; |
2702 | default: |
2703 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2704 | break; |
2705 | } |
2706 | break; |
2707 | } |
2708 | default: |
2709 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2710 | break; |
2711 | } |
2712 | |
2713 | switch (t.internal_format) { |
2714 | case GL_RGBA80x8058: |
2715 | // Clear color needs to swizzle to BGRA. |
2716 | request_clear<uint32_t>(t, |
2717 | (color & 0xFF00FF00) | |
2718 | ((color << 16) & 0xFF0000) | |
2719 | ((color >> 16) & 0xFF), |
2720 | scissor); |
2721 | break; |
2722 | case GL_R80x8229: |
2723 | request_clear<uint8_t>(t, uint8_t(color & 0xFF), scissor); |
2724 | break; |
2725 | case GL_RG80x822B: |
2726 | request_clear<uint16_t>(t, uint16_t(color & 0xFFFF), scissor); |
2727 | break; |
2728 | default: |
2729 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2730 | break; |
2731 | } |
2732 | } |
2733 | |
2734 | void ClearTexImage(GLuint texture, GLint level, GLenum format, GLenum type, |
2735 | const void* data) { |
2736 | Texture& t = ctx->textures[texture]; |
2737 | IntRect scissor = t.offset_bounds(); |
2738 | ClearTexSubImage(texture, level, scissor.x0, scissor.y0, 0, scissor.width(), |
2739 | scissor.height(), 1, format, type, data); |
2740 | } |
2741 | |
2742 | void Clear(GLbitfield mask) { |
2743 | Framebuffer& fb = *get_framebuffer(GL_DRAW_FRAMEBUFFER0x8CA9, true); |
2744 | if ((mask & GL_COLOR_BUFFER_BIT0x00004000) && fb.color_attachment) { |
2745 | Texture& t = ctx->textures[fb.color_attachment]; |
2746 | IntRect scissor = ctx->scissortest |
2747 | ? ctx->scissor.intersection(t.offset_bounds()) |
2748 | : t.offset_bounds(); |
2749 | ClearTexSubImage(fb.color_attachment, 0, scissor.x0, scissor.y0, 0, |
2750 | scissor.width(), scissor.height(), 1, GL_RGBA0x1908, GL_FLOAT0x1406, |
2751 | ctx->clearcolor); |
2752 | } |
2753 | if ((mask & GL_DEPTH_BUFFER_BIT0x00000100) && fb.depth_attachment) { |
2754 | Texture& t = ctx->textures[fb.depth_attachment]; |
2755 | IntRect scissor = ctx->scissortest |
2756 | ? ctx->scissor.intersection(t.offset_bounds()) |
2757 | : t.offset_bounds(); |
2758 | ClearTexSubImage(fb.depth_attachment, 0, scissor.x0, scissor.y0, 0, |
2759 | scissor.width(), scissor.height(), 1, GL_DEPTH_COMPONENT0x1902, |
2760 | GL_DOUBLE0x1408, &ctx->cleardepth); |
2761 | } |
2762 | } |
2763 | |
2764 | void ClearColorRect(GLuint fbo, GLint xoffset, GLint yoffset, GLsizei width, |
2765 | GLsizei height, GLfloat r, GLfloat g, GLfloat b, |
2766 | GLfloat a) { |
2767 | GLfloat color[] = {r, g, b, a}; |
2768 | Framebuffer& fb = ctx->framebuffers[fbo]; |
2769 | Texture& t = ctx->textures[fb.color_attachment]; |
2770 | IntRect scissor = |
2771 | IntRect{xoffset, yoffset, xoffset + width, yoffset + height}.intersection( |
2772 | t.offset_bounds()); |
2773 | ClearTexSubImage(fb.color_attachment, 0, scissor.x0, scissor.y0, 0, |
2774 | scissor.width(), scissor.height(), 1, GL_RGBA0x1908, GL_FLOAT0x1406, |
2775 | color); |
2776 | } |
2777 | |
2778 | void InvalidateFramebuffer(GLenum target, GLsizei num_attachments, |
2779 | const GLenum* attachments) { |
2780 | Framebuffer* fb = get_framebuffer(target); |
2781 | if (!fb || num_attachments <= 0 || !attachments) { |
2782 | return; |
2783 | } |
2784 | for (GLsizei i = 0; i < num_attachments; i++) { |
2785 | switch (attachments[i]) { |
2786 | case GL_DEPTH_ATTACHMENT0x8D00: { |
2787 | Texture& t = ctx->textures[fb->depth_attachment]; |
2788 | t.set_cleared(false); |
2789 | break; |
2790 | } |
2791 | case GL_COLOR_ATTACHMENT00x8CE0: { |
2792 | Texture& t = ctx->textures[fb->color_attachment]; |
2793 | t.disable_delayed_clear(); |
2794 | break; |
2795 | } |
2796 | } |
2797 | } |
2798 | } |
2799 | |
2800 | void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, |
2801 | GLenum type, void* data) { |
2802 | Buffer* pbo = get_pixel_pack_buffer(); |
2803 | if (pbo) { |
2804 | data = pbo->get_data(data); |
2805 | } |
2806 | if (!data) return; |
2807 | Framebuffer* fb = get_framebuffer(GL_READ_FRAMEBUFFER0x8CA8); |
2808 | if (!fb) return; |
2809 | assert(format == GL_RED || format == GL_RGBA || format == GL_RGBA_INTEGER ||(static_cast <bool> (format == 0x1903 || format == 0x1908 || format == 0x8D99 || format == 0x80E1 || format == 0x8227) ? void (0) : __assert_fail ("format == GL_RED || format == GL_RGBA || format == GL_RGBA_INTEGER || format == GL_BGRA || format == GL_RG" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )) |
2810 | format == GL_BGRA || format == GL_RG)(static_cast <bool> (format == 0x1903 || format == 0x1908 || format == 0x8D99 || format == 0x80E1 || format == 0x8227) ? void (0) : __assert_fail ("format == GL_RED || format == GL_RGBA || format == GL_RGBA_INTEGER || format == GL_BGRA || format == GL_RG" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2811 | Texture& t = ctx->textures[fb->color_attachment]; |
2812 | if (!t.buf) return; |
2813 | prepare_texture(t); |
2814 | // debugf("read pixels %d, %d, %d, %d from fb %d with format %x\n", x, y, |
2815 | // width, height, ctx->read_framebuffer_binding, t.internal_format); |
2816 | x -= t.offset.x; |
2817 | y -= t.offset.y; |
2818 | assert(x >= 0 && y >= 0)(static_cast <bool> (x >= 0 && y >= 0) ? void (0) : __assert_fail ("x >= 0 && y >= 0", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
2819 | assert(x + width <= t.width)(static_cast <bool> (x + width <= t.width) ? void (0 ) : __assert_fail ("x + width <= t.width", __builtin_FILE ( ), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
2820 | assert(y + height <= t.height)(static_cast <bool> (y + height <= t.height) ? void ( 0) : __assert_fail ("y + height <= t.height", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
2821 | if (internal_format_for_data(format, type) != t.internal_format) { |
2822 | debugf("mismatched format for read pixels: %x vs %x\n", t.internal_format,printf("mismatched format for read pixels: %x vs %x\n", t.internal_format , internal_format_for_data(format, type)) |
2823 | internal_format_for_data(format, type))printf("mismatched format for read pixels: %x vs %x\n", t.internal_format , internal_format_for_data(format, type)); |
2824 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2825 | return; |
2826 | } |
2827 | // Only support readback conversions that are reversible |
2828 | assert(!format_requires_conversion(format, t.internal_format) ||(static_cast <bool> (!format_requires_conversion(format , t.internal_format) || bytes_for_internal_format(format) == t .bpp()) ? void (0) : __assert_fail ("!format_requires_conversion(format, t.internal_format) || bytes_for_internal_format(format) == t.bpp()" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )) |
2829 | bytes_for_internal_format(format) == t.bpp())(static_cast <bool> (!format_requires_conversion(format , t.internal_format) || bytes_for_internal_format(format) == t .bpp()) ? void (0) : __assert_fail ("!format_requires_conversion(format, t.internal_format) || bytes_for_internal_format(format) == t.bpp()" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2830 | uint8_t* dest = (uint8_t*)data; |
2831 | size_t destStride = width * t.bpp(); |
2832 | if (y < 0) { |
2833 | dest += -y * destStride; |
2834 | height += y; |
2835 | y = 0; |
2836 | } |
2837 | if (y + height > t.height) { |
2838 | height = t.height - y; |
2839 | } |
2840 | if (x < 0) { |
2841 | dest += -x * t.bpp(); |
2842 | width += x; |
2843 | x = 0; |
2844 | } |
2845 | if (x + width > t.width) { |
2846 | width = t.width - x; |
2847 | } |
2848 | if (width <= 0 || height <= 0) { |
2849 | return; |
2850 | } |
2851 | convert_copy(format, t.internal_format, dest, destStride, |
2852 | pbo ? (uint8_t*)pbo->buf : nullptr, |
2853 | pbo ? (uint8_t*)pbo->end_ptr() : nullptr, |
2854 | (const uint8_t*)t.sample_ptr(x, y), t.stride(), |
2855 | (const uint8_t*)t.buf, (const uint8_t*)t.end_ptr(), width, |
2856 | height); |
2857 | } |
2858 | |
2859 | void CopyImageSubData(GLuint srcName, GLenum srcTarget, UNUSED[[maybe_unused]] GLint srcLevel, |
2860 | GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, |
2861 | GLenum dstTarget, UNUSED[[maybe_unused]] GLint dstLevel, GLint dstX, |
2862 | GLint dstY, GLint dstZ, GLsizei srcWidth, |
2863 | GLsizei srcHeight, GLsizei srcDepth) { |
2864 | assert(srcLevel == 0 && dstLevel == 0)(static_cast <bool> (srcLevel == 0 && dstLevel == 0) ? void (0) : __assert_fail ("srcLevel == 0 && dstLevel == 0" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2865 | assert(srcZ == 0 && srcDepth == 1 && dstZ == 0)(static_cast <bool> (srcZ == 0 && srcDepth == 1 && dstZ == 0) ? void (0) : __assert_fail ("srcZ == 0 && srcDepth == 1 && dstZ == 0" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2866 | if (srcTarget == GL_RENDERBUFFER0x8D41) { |
2867 | Renderbuffer& rb = ctx->renderbuffers[srcName]; |
2868 | srcName = rb.texture; |
2869 | } |
2870 | if (dstTarget == GL_RENDERBUFFER0x8D41) { |
2871 | Renderbuffer& rb = ctx->renderbuffers[dstName]; |
2872 | dstName = rb.texture; |
2873 | } |
2874 | Texture& srctex = ctx->textures[srcName]; |
2875 | if (!srctex.buf) return; |
2876 | prepare_texture(srctex); |
2877 | Texture& dsttex = ctx->textures[dstName]; |
2878 | if (!dsttex.buf) return; |
2879 | assert(!dsttex.locked)(static_cast <bool> (!dsttex.locked) ? void (0) : __assert_fail ("!dsttex.locked", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
2880 | IntRect skip = {dstX, dstY, dstX + srcWidth, dstY + srcHeight}; |
2881 | prepare_texture(dsttex, &skip); |
2882 | assert(srctex.internal_format == dsttex.internal_format)(static_cast <bool> (srctex.internal_format == dsttex.internal_format ) ? void (0) : __assert_fail ("srctex.internal_format == dsttex.internal_format" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2883 | assert(srcWidth >= 0)(static_cast <bool> (srcWidth >= 0) ? void (0) : __assert_fail ("srcWidth >= 0", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
2884 | assert(srcHeight >= 0)(static_cast <bool> (srcHeight >= 0) ? void (0) : __assert_fail ("srcHeight >= 0", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
2885 | assert(srcX + srcWidth <= srctex.width)(static_cast <bool> (srcX + srcWidth <= srctex.width ) ? void (0) : __assert_fail ("srcX + srcWidth <= srctex.width" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2886 | assert(srcY + srcHeight <= srctex.height)(static_cast <bool> (srcY + srcHeight <= srctex.height ) ? void (0) : __assert_fail ("srcY + srcHeight <= srctex.height" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2887 | assert(dstX + srcWidth <= dsttex.width)(static_cast <bool> (dstX + srcWidth <= dsttex.width ) ? void (0) : __assert_fail ("dstX + srcWidth <= dsttex.width" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2888 | assert(dstY + srcHeight <= dsttex.height)(static_cast <bool> (dstY + srcHeight <= dsttex.height ) ? void (0) : __assert_fail ("dstY + srcHeight <= dsttex.height" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2889 | int bpp = srctex.bpp(); |
2890 | int src_stride = srctex.stride(); |
2891 | int dest_stride = dsttex.stride(); |
2892 | char* dest = dsttex.sample_ptr(dstX, dstY); |
2893 | const char* src = srctex.sample_ptr(srcX, srcY); |
2894 | for (int y = 0; y < srcHeight; y++) { |
2895 | char* dst_ptr = dest; |
2896 | const char* src_ptr = src; |
2897 | size_t len = size_t(srcWidth) * bpp; |
2898 | if (clip_ptrs_against_bounds(dst_ptr, dsttex.buf, dsttex.end_ptr(), src_ptr, |
2899 | srctex.buf, srctex.end_ptr(), len) > 0) { |
2900 | break; |
2901 | } |
2902 | if (len) { |
2903 | memcpy(dst_ptr, src_ptr, len); |
2904 | } |
2905 | dest += dest_stride; |
2906 | src += src_stride; |
2907 | } |
2908 | } |
2909 | |
2910 | void CopyTexSubImage2D(GLenum target, UNUSED[[maybe_unused]] GLint level, GLint xoffset, |
2911 | GLint yoffset, GLint x, GLint y, GLsizei width, |
2912 | GLsizei height) { |
2913 | assert(level == 0)(static_cast <bool> (level == 0) ? void (0) : __assert_fail ("level == 0", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
2914 | Framebuffer* fb = get_framebuffer(GL_READ_FRAMEBUFFER0x8CA8); |
2915 | if (!fb) return; |
2916 | CopyImageSubData(fb->color_attachment, GL_TEXTURE_2D0x0DE1, 0, x, y, 0, |
2917 | ctx->get_binding(target), GL_TEXTURE_2D0x0DE1, 0, xoffset, yoffset, |
2918 | 0, width, height, 1); |
2919 | } |
2920 | |
2921 | } // extern "C" |
2922 | |
2923 | #include "blend.h" |
2924 | #include "composite.h" |
2925 | #include "swgl_ext.h" |
2926 | |
2927 | #pragma GCC diagnostic push |
2928 | #pragma GCC diagnostic ignored "-Wuninitialized" |
2929 | #pragma GCC diagnostic ignored "-Wunused-function" |
2930 | #pragma GCC diagnostic ignored "-Wunused-parameter" |
2931 | #pragma GCC diagnostic ignored "-Wunused-variable" |
2932 | #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" |
2933 | #ifdef __clang__1 |
2934 | # pragma GCC diagnostic ignored "-Wunused-private-field" |
2935 | #else |
2936 | # pragma GCC diagnostic ignored "-Wunused-but-set-variable" |
2937 | #endif |
2938 | #include "load_shader.h" |
2939 | #pragma GCC diagnostic pop |
2940 | |
2941 | #include "rasterize.h" |
2942 | |
2943 | void VertexArray::validate() { |
2944 | int last_enabled = -1; |
2945 | for (int i = 0; i <= max_attrib; i++) { |
2946 | VertexAttrib& attr = attribs[i]; |
2947 | if (attr.enabled) { |
2948 | // VertexArray &v = ctx->vertex_arrays[attr.vertex_array]; |
2949 | Buffer& vertex_buf = ctx->buffers[attr.vertex_buffer]; |
2950 | attr.buf = vertex_buf.buf; |
2951 | attr.buf_size = vertex_buf.size; |
2952 | // debugf("%d %x %d %d %d %d\n", i, attr.type, attr.size, attr.stride, |
2953 | // attr.offset, attr.divisor); |
2954 | last_enabled = i; |
2955 | } |
2956 | } |
2957 | max_attrib = last_enabled; |
2958 | } |
2959 | |
2960 | extern "C" { |
2961 | |
2962 | void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, |
2963 | GLintptr offset, GLsizei instancecount) { |
2964 | if (offset < 0 || count <= 0 || instancecount <= 0 || !vertex_shader || |
2965 | !fragment_shader) { |
2966 | return; |
2967 | } |
2968 | |
2969 | Framebuffer& fb = *get_framebuffer(GL_DRAW_FRAMEBUFFER0x8CA9, true); |
2970 | if (!fb.color_attachment) { |
2971 | return; |
2972 | } |
2973 | Texture& colortex = ctx->textures[fb.color_attachment]; |
2974 | if (!colortex.buf) { |
2975 | return; |
2976 | } |
2977 | assert(!colortex.locked)(static_cast <bool> (!colortex.locked) ? void (0) : __assert_fail ("!colortex.locked", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
2978 | assert(colortex.internal_format == GL_RGBA8 ||(static_cast <bool> (colortex.internal_format == 0x8058 || colortex.internal_format == 0x8229) ? void (0) : __assert_fail ("colortex.internal_format == GL_RGBA8 || colortex.internal_format == GL_R8" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )) |
2979 | colortex.internal_format == GL_R8)(static_cast <bool> (colortex.internal_format == 0x8058 || colortex.internal_format == 0x8229) ? void (0) : __assert_fail ("colortex.internal_format == GL_RGBA8 || colortex.internal_format == GL_R8" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2980 | Texture& depthtex = ctx->textures[ctx->depthtest ? fb.depth_attachment : 0]; |
2981 | if (depthtex.buf) { |
2982 | assert(depthtex.internal_format == GL_DEPTH_COMPONENT24)(static_cast <bool> (depthtex.internal_format == 0x81A6 ) ? void (0) : __assert_fail ("depthtex.internal_format == GL_DEPTH_COMPONENT24" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2983 | assert(colortex.width == depthtex.width &&(static_cast <bool> (colortex.width == depthtex.width && colortex.height == depthtex.height) ? void (0) : __assert_fail ("colortex.width == depthtex.width && colortex.height == depthtex.height" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )) |
2984 | colortex.height == depthtex.height)(static_cast <bool> (colortex.width == depthtex.width && colortex.height == depthtex.height) ? void (0) : __assert_fail ("colortex.width == depthtex.width && colortex.height == depthtex.height" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2985 | assert(colortex.offset == depthtex.offset)(static_cast <bool> (colortex.offset == depthtex.offset ) ? void (0) : __assert_fail ("colortex.offset == depthtex.offset" , __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
2986 | } |
2987 | |
2988 | // debugf("current_vertex_array %d\n", ctx->current_vertex_array); |
2989 | // debugf("indices size: %d\n", indices_buf.size); |
2990 | VertexArray& v = ctx->vertex_arrays[ctx->current_vertex_array]; |
2991 | if (ctx->validate_vertex_array) { |
2992 | ctx->validate_vertex_array = false; |
2993 | v.validate(); |
2994 | } |
2995 | |
2996 | #ifdef PRINT_TIMINGS |
2997 | uint64_t start = get_time_value(); |
2998 | #endif |
2999 | |
3000 | ctx->shaded_rows = 0; |
3001 | ctx->shaded_pixels = 0; |
3002 | |
3003 | vertex_shader->init_batch(); |
3004 | |
3005 | switch (type) { |
3006 | case GL_UNSIGNED_SHORT0x1403: |
3007 | assert(mode == GL_TRIANGLES)(static_cast <bool> (mode == 0x0004) ? void (0) : __assert_fail ("mode == GL_TRIANGLES", __builtin_FILE (), __builtin_LINE ( ), __extension__ __PRETTY_FUNCTION__)); |
3008 | draw_elements<uint16_t>(count, instancecount, offset, v, colortex, |
3009 | depthtex); |
3010 | break; |
3011 | case GL_UNSIGNED_INT0x1405: |
3012 | assert(mode == GL_TRIANGLES)(static_cast <bool> (mode == 0x0004) ? void (0) : __assert_fail ("mode == GL_TRIANGLES", __builtin_FILE (), __builtin_LINE ( ), __extension__ __PRETTY_FUNCTION__)); |
3013 | draw_elements<uint32_t>(count, instancecount, offset, v, colortex, |
3014 | depthtex); |
3015 | break; |
3016 | case GL_NONE0: |
3017 | // Non-standard GL extension - if element type is GL_NONE, then we don't |
3018 | // use any element buffer and behave as if DrawArrays was called instead. |
3019 | for (GLsizei instance = 0; instance < instancecount; instance++) { |
3020 | switch (mode) { |
3021 | case GL_LINES0x0001: |
3022 | for (GLsizei i = 0; i + 2 <= count; i += 2) { |
3023 | vertex_shader->load_attribs(v.attribs, offset + i, instance, 2); |
3024 | draw_quad(2, colortex, depthtex); |
3025 | } |
3026 | break; |
3027 | case GL_TRIANGLES0x0004: |
3028 | for (GLsizei i = 0; i + 3 <= count; i += 3) { |
3029 | vertex_shader->load_attribs(v.attribs, offset + i, instance, 3); |
3030 | draw_quad(3, colortex, depthtex); |
3031 | } |
3032 | break; |
3033 | default: |
3034 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
3035 | break; |
3036 | } |
3037 | } |
3038 | break; |
3039 | default: |
3040 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__ )); |
3041 | break; |
3042 | } |
3043 | |
3044 | if (ctx->samples_passed_query) { |
3045 | Query& q = ctx->queries[ctx->samples_passed_query]; |
3046 | q.value += ctx->shaded_pixels; |
3047 | } |
3048 | |
3049 | #ifdef PRINT_TIMINGS |
3050 | uint64_t end = get_time_value(); |
3051 | printf( |
3052 | "%7.3fms draw(%s, %d): %d pixels in %d rows (avg %f pixels/row, " |
3053 | "%fns/pixel)\n", |
3054 | double(end - start) / (1000. * 1000.), |
3055 | ctx->programs[ctx->current_program].impl->get_name(), instancecount, |
3056 | ctx->shaded_pixels, ctx->shaded_rows, |
3057 | double(ctx->shaded_pixels) / ctx->shaded_rows, |
3058 | double(end - start) / max(ctx->shaded_pixels, 1)); |
3059 | #endif |
3060 | } |
3061 | |
3062 | void Finish() { |
3063 | #ifdef PRINT_TIMINGS |
3064 | printf("Finish\n"); |
3065 | #endif |
3066 | } |
3067 | |
3068 | void MakeCurrent(Context* c) { |
3069 | if (ctx == c) { |
3070 | return; |
3071 | } |
3072 | ctx = c; |
3073 | setup_program(ctx ? ctx->current_program : 0); |
3074 | } |
3075 | |
3076 | Context* CreateContext() { return new Context; } |
3077 | |
3078 | void ReferenceContext(Context* c) { |
3079 | if (!c) { |
3080 | return; |
3081 | } |
3082 | ++c->references; |
3083 | } |
3084 | |
3085 | void DestroyContext(Context* c) { |
3086 | if (!c) { |
3087 | return; |
3088 | } |
3089 | assert(c->references > 0)(static_cast <bool> (c->references > 0) ? void (0 ) : __assert_fail ("c->references > 0", __builtin_FILE ( ), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); |
3090 | --c->references; |
3091 | if (c->references > 0) { |
3092 | return; |
3093 | } |
3094 | if (ctx == c) { |
3095 | MakeCurrent(nullptr); |
3096 | } |
3097 | delete c; |
3098 | } |
3099 | |
3100 | size_t ReportMemory(Context* ctx, size_t (*size_of_op)(const void*)) { |
3101 | size_t size = 0; |
3102 | if (ctx) { |
3103 | for (auto& t : ctx->textures) { |
3104 | if (t && t->should_free()) { |
3105 | size += size_of_op(t->buf); |
3106 | } |
3107 | } |
3108 | } |
3109 | return size; |
3110 | } |
3111 | } // extern "C" |