Bug Summary

File:root/firefox-clang/gfx/wr/swgl/src/gl.cc
Warning:line 779, 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

Annotated Source Code

Press '?' to see keyboard shortcuts

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