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