mirror of
https://github.com/libretro/parallel-n64.git
synced 2026-11-07 04:25:59 -10:00
glsm: restore all of the core's GL state, and keep the cache true to GL
glsm skips a GL call whose value it has cached, and puts the core's state back after the frontend draws between slices; both went wrong. The bind only re-enabled the core's capabilities, so one the frontend left on - blending, by RetroArch's statistics overlay among others - stayed on, and the cache, saying off, swallowed the core's glDisable: Glide64, Rice and glN64 blended each frame into the one before it. The cull face was never restored, nor the blend equation, colour or line width; the clear colour was cached in integers and only while the default framebuffer was bound; glBlendFunc's cache never saw glBlendFuncSeparate; a texture deleted on another unit, a deleted buffer or framebuffer stayed cached as bound, so the name GL gave again was not bound; a vertex array's element buffer was cached across vertex arrays and written into the next one at the bind; glDrawBuffers was taken once per framebuffer and ignored after; and the unbind left a stencil write mask of 1. The cache now starts from GL's defaults, the bind puts all of it in GL and the unbind GL's defaults, and every deletion and vertex array bind keeps it to what GL holds. Fog and alpha test are not passed to core and ES contexts, where they are no capabilities. The framebuffer records were a 128000-entry table (1 MB, scanned in full for every deleted texture) and the uniform cache a static 64 MB; they are a small list and per-program tables allocated on first use, dropped when a program links or is deleted and with the context. glBlitFramebuffer is no longer turned into glCopyImageSubData from attachments glsm could not know for the frontend's framebuffers. Dead debug logging, no-op VBO calls and the unused extension helper are gone, and the files build clean as C89 bar one cast in the public API. test_glsm_state, for desktop GL and GLES 2, has the core set its state, the frontend change all of it between slices, and holds GL to the core's state after the bind and to GL's defaults after the unbind, then runs each cache case above; master fails it. It and test_glsm_frame_boundary, which builds again (it needed gl_capabilities.c and screen_width since the draw target), run in the GitLab GL job and the GitHub GL renderers workflow, with and without the sanitizers.
This commit is contained in:
1 parent
c93f3ae88a
commit
a3283a7c80
10 files changed
+929
-1090
No files matched your search
@@ -14,7 +14,8 @@
|
||||
# (test_gliden64_ring_segments). test_glsm_draw_target(_gles) holds the
|
||||
# frame Glide64, Rice and glN64 swap to the one the frontend presents,
|
||||
# not the next one they go on to draw over it, through the blit and
|
||||
# through GLES 2's texture copy; the Glitch64 tests hold its LFB write
|
||||
# through GLES 2's texture copy; test_glsm_state(_gles) holds the GL
|
||||
# state glsm restores for the core to what the core set; the Glitch64 tests hold its LFB write
|
||||
# and texture uploads. The sanitize leg runs it all again with
|
||||
# AddressSanitizer and UndefinedBehaviorSanitizer: any report fails.
|
||||
name: GL renderers
|
||||
@@ -45,7 +46,7 @@ jobs:
|
||||
env:
|
||||
MAKEFLAGS: --no-print-directory
|
||||
CORE_FLAGS: platform=unix WITH_DYNAREC=x86_64 HAVE_THR_AL=1 HAVE_PARALLEL=1 HAVE_PARALLEL_RSP=1
|
||||
GL_TESTS: test_glitch64_lfb_write test_glitch64_textures test_glitch64_textures_gles test_glsm_draw_target test_glsm_draw_target_gles
|
||||
GL_TESTS: test_glitch64_lfb_write test_glitch64_textures test_glitch64_textures_gles test_glsm_draw_target test_glsm_draw_target_gles test_glsm_state test_glsm_state_gles
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@@ -66,7 +67,9 @@ jobs:
|
||||
make platform=unix SANITIZER=${{ matrix.sanitizer }} lrhost_gl $GL_TESTS
|
||||
|
||||
- name: GL unit tests
|
||||
run: for t in $GL_TESTS; do env EGL_PLATFORM=surfaceless ${{ matrix.sanitizer_env }} ./$t || exit 1; done
|
||||
run: |
|
||||
for t in $GL_TESTS; do env EGL_PLATFORM=surfaceless ${{ matrix.sanitizer_env }} ./$t || exit 1; done
|
||||
python3 tools/test_glsm_frame_boundary.py
|
||||
|
||||
- name: krom ROMs cache
|
||||
uses: actions/cache@v4
|
||||
|
||||
+7
-5
@@ -212,7 +212,8 @@ test-parallel-rdp-tsan-linux-x64:
|
||||
# uploads, on GL and on GLES 2, to what each Glide format means.
|
||||
# test_glsm_draw_target(_gles) holds the frame Glide64, Rice and glN64
|
||||
# swap to the one the frontend presents, through the blit and through
|
||||
# GLES 2's texture copy. tools/lrhost runs them through angrylion with and
|
||||
# GLES 2's texture copy, and test_glsm_state(_gles) the GL state glsm
|
||||
# restores for the core to what the core set. tools/lrhost runs them through angrylion with and
|
||||
# without a lent software framebuffer, for each VI output: the pictures
|
||||
# must match, and the modes that can draw into the lent buffer must. Then
|
||||
# both again with the core and hosts built with AddressSanitizer and
|
||||
@@ -232,14 +233,15 @@ test-gl-renderers-linux-x64:
|
||||
- apt-get update -qq && apt-get install -y -qq build-essential nasm git ca-certificates curl python3 libgl-dev libegl-dev libgles-dev libegl-mesa0 libgl1-mesa-dri
|
||||
- make -j$(nproc) platform=unix WITH_DYNAREC=$WITH_DYNAREC HAVE_THR_AL=$HAVE_THR_AL HAVE_PARALLEL=$HAVE_PARALLEL HAVE_PARALLEL_RSP=$HAVE_PARALLEL_RSP
|
||||
- python3 tools/ci/check_vtables.py
|
||||
- make platform=unix lrhost_gl lrhost test_glitch64_lfb_write test_glitch64_textures test_glitch64_textures_gles test_glsm_draw_target test_glsm_draw_target_gles
|
||||
- for t in test_glitch64_lfb_write test_glitch64_textures test_glitch64_textures_gles test_glsm_draw_target test_glsm_draw_target_gles; do EGL_PLATFORM=surfaceless ./$t || exit 1; done
|
||||
- make platform=unix lrhost_gl lrhost test_glitch64_lfb_write test_glitch64_textures test_glitch64_textures_gles test_glsm_draw_target test_glsm_draw_target_gles test_glsm_state test_glsm_state_gles
|
||||
- for t in test_glitch64_lfb_write test_glitch64_textures test_glitch64_textures_gles test_glsm_draw_target test_glsm_draw_target_gles test_glsm_state test_glsm_state_gles; do EGL_PLATFORM=surfaceless ./$t || exit 1; done
|
||||
- python3 tools/test_glsm_frame_boundary.py
|
||||
- python3 tools/ci/gl_renderers.py --core ./parallel_n64_libretro.so --host ./lrhost_gl
|
||||
- python3 tools/ci/angrylion_lend.py --core ./parallel_n64_libretro.so --host ./lrhost
|
||||
- make platform=unix clean
|
||||
- make -j$(nproc) platform=unix WITH_DYNAREC=$WITH_DYNAREC HAVE_THR_AL=$HAVE_THR_AL HAVE_PARALLEL=$HAVE_PARALLEL HAVE_PARALLEL_RSP=$HAVE_PARALLEL_RSP SANITIZER=address,undefined
|
||||
- make platform=unix SANITIZER=address,undefined lrhost_gl lrhost test_glitch64_lfb_write test_glitch64_textures test_glitch64_textures_gles test_glsm_draw_target test_glsm_draw_target_gles
|
||||
- for t in test_glitch64_lfb_write test_glitch64_textures test_glitch64_textures_gles test_glsm_draw_target test_glsm_draw_target_gles; do EGL_PLATFORM=surfaceless ASAN_OPTIONS=detect_leaks=0 UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1 ./$t || exit 1; done
|
||||
- make platform=unix SANITIZER=address,undefined lrhost_gl lrhost test_glitch64_lfb_write test_glitch64_textures test_glitch64_textures_gles test_glsm_draw_target test_glsm_draw_target_gles test_glsm_state test_glsm_state_gles
|
||||
- for t in test_glitch64_lfb_write test_glitch64_textures test_glitch64_textures_gles test_glsm_draw_target test_glsm_draw_target_gles test_glsm_state test_glsm_state_gles; do EGL_PLATFORM=surfaceless ASAN_OPTIONS=detect_leaks=0 UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1 ./$t || exit 1; done
|
||||
- ASAN_OPTIONS=detect_leaks=0 UBSAN_OPTIONS=print_stacktrace=1 python3 tools/ci/gl_renderers.py --core ./parallel_n64_libretro.so --host ./lrhost_gl
|
||||
- ASAN_OPTIONS=detect_leaks=0 UBSAN_OPTIONS=print_stacktrace=1 python3 tools/ci/angrylion_lend.py --core ./parallel_n64_libretro.so --host ./lrhost
|
||||
|
||||
|
||||
@@ -1274,7 +1274,8 @@ test_glitch64_textures_gles$(EXE_EXT): tools/test_glitch64_textures.c $(GLITCH64
|
||||
$(CC) $(GLITCH64_TEST_FLAGS) $(GLITCH64_GLES_FLAGS) $< $(GLITCH64_TEST_SRCS) $(LIBRETRO_COMM_DIR)/glsym/glsym_es2.c \
|
||||
-o $@ $(filter -fsanitize=%,$(LDFLAGS)) -lEGL -lGLESv2 -lm
|
||||
# glsm's draw target: the frontend presents the frame a renderer swapped,
|
||||
# through the blit (desktop GL) and the texture copy (GLES 2)
|
||||
# through the blit (desktop GL) and the texture copy (GLES 2); and the
|
||||
# state glsm keeps is the state GL holds while the core draws
|
||||
GLSM_TEST_SRCS := $(ROOT_DIR)/libretro/glsm/glsm.c $(LIBRETRO_COMM_DIR)/gfx/gl_capabilities.c $(LIBRETRO_COMM_DIR)/glsym/rglgen.c
|
||||
GLSM_TEST_FLAGS := -D__LIBRETRO__ -I$(ROOT_DIR)/libretro -I$(LIBRETRO_COMM_DIR)/include \
|
||||
$(filter -fsanitize=% -O% -g,$(CFLAGS)) -w
|
||||
@@ -1285,8 +1286,15 @@ test_glsm_draw_target_gles$(EXE_EXT): tools/test_glsm_draw_target.c $(GLSM_TEST_
|
||||
$(CC) $(GLSM_TEST_FLAGS) -DHAVE_OPENGLES -DHAVE_OPENGLES2 $< $(GLSM_TEST_SRCS) $(LIBRETRO_COMM_DIR)/glsym/glsym_es2.c \
|
||||
-o $@ $(filter -fsanitize=%,$(LDFLAGS)) -lEGL -lGLESv2 -lm
|
||||
|
||||
test_glsm_state$(EXE_EXT): tools/test_glsm_state.c $(GLSM_TEST_SRCS)
|
||||
$(CC) $(GLSM_TEST_FLAGS) -DHAVE_OPENGL $< $(GLSM_TEST_SRCS) $(LIBRETRO_COMM_DIR)/glsym/glsym_gl.c \
|
||||
-o $@ $(filter -fsanitize=%,$(LDFLAGS)) -lEGL -lGL -lm
|
||||
test_glsm_state_gles$(EXE_EXT): tools/test_glsm_state.c $(GLSM_TEST_SRCS)
|
||||
$(CC) $(GLSM_TEST_FLAGS) -DHAVE_OPENGLES -DHAVE_OPENGLES2 $< $(GLSM_TEST_SRCS) $(LIBRETRO_COMM_DIR)/glsym/glsym_es2.c \
|
||||
-o $@ $(filter -fsanitize=%,$(LDFLAGS)) -lEGL -lGLESv2 -lm
|
||||
|
||||
GLITCH64_TESTS := test_glitch64_lfb_write$(EXE_EXT) test_glitch64_textures$(EXE_EXT) test_glitch64_textures_gles$(EXE_EXT) \
|
||||
test_glsm_draw_target$(EXE_EXT) test_glsm_draw_target_gles$(EXE_EXT)
|
||||
test_glsm_draw_target$(EXE_EXT) test_glsm_draw_target_gles$(EXE_EXT) test_glsm_state$(EXE_EXT) test_glsm_state_gles$(EXE_EXT)
|
||||
|
||||
angrylion_replay$(EXE_EXT): tools/angrylion_replay.c $(ANGRYLION_TOOL_OBJS)
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -I$(VIDEODIR_ANGRYLION) -w $< $(ANGRYLION_TOOL_OBJS) -o $@ -lpthread -lm
|
||||
|
||||
+572
-1076
File diff suppressed because it is too large.
Load diff
@@ -46,7 +46,7 @@ typedef double GLdouble;
|
||||
#undef GL_DRAW_FRAMEBUFFER_BINDING
|
||||
#undef GL_COPY_READ_BUFFER_BINDING
|
||||
#undef GL_COPY_WRITE_BUFFER_BINDING
|
||||
#endif // HAVE_OPENGLES3
|
||||
#endif /* HAVE_OPENGLES3 */
|
||||
|
||||
#if defined(HAVE_OPENGLES2)
|
||||
#define RARCH_GL_RENDERBUFFER GL_RENDERBUFFER
|
||||
|
||||
@@ -52,4 +52,4 @@ enum
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // LIBRETRO_SDK_GLSM_CAPS_H
|
||||
#endif /* LIBRETRO_SDK_GLSM_CAPS_H */
|
||||
@@ -50,4 +50,4 @@ bool glsm_ctl(enum glsm_state_ctl state, void *data);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // LIBRETRO_SDK_GLSM_STATE_CTL_H
|
||||
#endif /* LIBRETRO_SDK_GLSM_STATE_CTL_H */
|
||||
@@ -39,6 +39,7 @@ def main():
|
||||
str(root / "tools/test_glsm_gles3.c"),
|
||||
str(root / "libretro-common/glsym/glsym_es3.c"),
|
||||
str(root / "libretro-common/glsym/rglgen.c"),
|
||||
str(root / "libretro-common/gfx/gl_capabilities.c"),
|
||||
"-lEGL", "-lGLESv2", "-o", str(binary),
|
||||
], check=True)
|
||||
subprocess.run([str(binary)], check=True,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* cc -std=c99 -DHAVE_OPENGLES -DHAVE_OPENGLES3 -Ilibretro-common/include -Ilibretro
|
||||
* tools/test_glsm_gles3.c libretro-common/glsym/glsym_es3.c
|
||||
* libretro-common/glsym/rglgen.c -lEGL -lGLESv2 -o test_glsm_gles3
|
||||
* libretro-common/glsym/rglgen.c libretro-common/gfx/gl_capabilities.c
|
||||
* -lEGL -lGLESv2 -o test_glsm_gles3
|
||||
* EGL_PLATFORM=surfaceless ./test_glsm_gles3
|
||||
*/
|
||||
#include <assert.h>
|
||||
@@ -14,6 +15,7 @@
|
||||
#ifdef GLSM_FRAME_BOUNDARY_SOURCE
|
||||
#include "../Graphics/plugin.h"
|
||||
static int g_real_stop;
|
||||
static unsigned screen_width = 16, screen_height = 16;
|
||||
enum gfx_plugin_type gfx_plugin = GFX_GLIDEN64;
|
||||
#include GLSM_FRAME_BOUNDARY_SOURCE
|
||||
#else
|
||||
|
||||
@@ -0,0 +1,327 @@
|
||||
/* test_glsm_state -- the GL state glsm keeps for the core is the state GL
|
||||
* holds while the core draws.
|
||||
*
|
||||
* glsm caches the core's GL state and skips a call whose value it has
|
||||
* cached. Between slices the frontend draws with the same context, so
|
||||
* glsm_state_bind must put the core's state back over whatever the
|
||||
* frontend left, and the cache must follow GL wherever GL changes on its
|
||||
* own. It did not: a capability the frontend left enabled (blending, by
|
||||
* the statistics overlay among others) stayed enabled, as glsm only
|
||||
* re-enabled the core's; the clear colour was cached in integers, and
|
||||
* only for the default framebuffer; glBlendFunc's cache never saw
|
||||
* glBlendFuncSeparate; the cull face was never restored; a texture
|
||||
* deleted on another unit, or a deleted buffer, stayed cached as bound,
|
||||
* so the name GL gave again was never bound; a vertex array's element
|
||||
* buffer was cached across vertex arrays; glDrawBuffers was set once per
|
||||
* framebuffer and ignored after. Each made the core draw with state it
|
||||
* had not asked for: the frames of Glide64, Rice and glN64 blended into
|
||||
* the ones before them.
|
||||
*
|
||||
* Here the core sets its state through glsm, the frontend changes all of
|
||||
* it between slices, and after the bind every piece must read back as
|
||||
* the core set it, and after the unbind as GL's defaults. Then each
|
||||
* deletion and cache case above must leave GL holding what the core
|
||||
* asked for. Built for desktop GL and for GLES 2, on Mesa through
|
||||
* headless EGL:
|
||||
* EGL_PLATFORM=surfaceless ./test_glsm_state
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <EGL/egl.h>
|
||||
#include <glsym/glsym.h>
|
||||
#include <glsm/glsm.h>
|
||||
#include <libretro.h>
|
||||
|
||||
retro_log_printf_t log_cb;
|
||||
|
||||
static GLuint frontend_fbo, frontend_tex;
|
||||
static int failures;
|
||||
|
||||
static uintptr_t framebuffer(void) { return frontend_fbo; }
|
||||
static retro_proc_address_t resolve(const char *name)
|
||||
{
|
||||
return (retro_proc_address_t)eglGetProcAddress(name);
|
||||
}
|
||||
|
||||
static bool environment(unsigned cmd, void *data)
|
||||
{
|
||||
if (cmd == RETRO_ENVIRONMENT_SET_HW_RENDER)
|
||||
{
|
||||
struct retro_hw_render_callback *hw = (struct retro_hw_render_callback*)data;
|
||||
hw->get_proc_address = resolve;
|
||||
hw->get_current_framebuffer = framebuffer;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* GL as it stands, read past glsm */
|
||||
static GLint gl_int(GLenum pname)
|
||||
{
|
||||
GLint v = -1;
|
||||
glGetIntegerv(pname, &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
static float gl_float(GLenum pname, unsigned i)
|
||||
{
|
||||
GLfloat v[4] = { -1, -1, -1, -1 };
|
||||
glGetFloatv(pname, v);
|
||||
return v[i];
|
||||
}
|
||||
|
||||
static GLboolean gl_bool(GLenum pname, unsigned i)
|
||||
{
|
||||
GLboolean v[4] = { 9, 9, 9, 9 };
|
||||
glGetBooleanv(pname, v);
|
||||
return v[i];
|
||||
}
|
||||
|
||||
static int gl_enabled(GLenum cap) { return glIsEnabled(cap) ? 1 : 0; }
|
||||
|
||||
#define CHECK(what, cond) do { if (!(cond)) { \
|
||||
fprintf(stderr, "%s: %s does not hold (%s:%d)\n", what, #cond, __FILE__, __LINE__); \
|
||||
failures++; } } while (0)
|
||||
|
||||
/* the frontend's half between slices: every piece of state changed */
|
||||
static void frontend_draws(void)
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, frontend_fbo);
|
||||
glEnable(GL_BLEND);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glEnable(GL_CULL_FACE);
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glBlendFuncSeparate(GL_ONE, GL_ONE, GL_ZERO, GL_ONE);
|
||||
glBlendEquationSeparate(GL_FUNC_SUBTRACT, GL_FUNC_REVERSE_SUBTRACT);
|
||||
glBlendColor(0.5f, 0.5f, 0.5f, 0.5f);
|
||||
glColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_TRUE);
|
||||
glDepthMask(GL_FALSE);
|
||||
glDepthFunc(GL_ALWAYS);
|
||||
glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
|
||||
glCullFace(GL_FRONT);
|
||||
glFrontFace(GL_CW);
|
||||
glStencilFunc(GL_NEVER, 3, 7);
|
||||
glStencilOp(GL_ZERO, GL_INCR, GL_DECR);
|
||||
glStencilMask(5);
|
||||
glPolygonOffset(3.0f, 3.0f);
|
||||
glScissor(1, 2, 3, 4);
|
||||
glViewport(5, 6, 7, 8);
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
/* the core's state, as GL must hold it while the core draws */
|
||||
static void check_core_state(const char *when)
|
||||
{
|
||||
CHECK(when, !gl_enabled(GL_BLEND));
|
||||
CHECK(when, !gl_enabled(GL_SCISSOR_TEST));
|
||||
CHECK(when, !gl_enabled(GL_STENCIL_TEST));
|
||||
CHECK(when, !gl_enabled(GL_POLYGON_OFFSET_FILL));
|
||||
CHECK(when, gl_enabled(GL_DEPTH_TEST));
|
||||
CHECK(when, gl_enabled(GL_CULL_FACE));
|
||||
CHECK(when, gl_int(GL_BLEND_SRC_RGB) == GL_SRC_ALPHA);
|
||||
CHECK(when, gl_int(GL_BLEND_DST_RGB) == GL_ONE_MINUS_SRC_ALPHA);
|
||||
CHECK(when, gl_int(GL_BLEND_SRC_ALPHA) == GL_SRC_ALPHA);
|
||||
CHECK(when, gl_int(GL_BLEND_DST_ALPHA) == GL_ONE_MINUS_SRC_ALPHA);
|
||||
CHECK(when, gl_int(GL_BLEND_EQUATION_RGB) == GL_FUNC_ADD);
|
||||
CHECK(when, gl_int(GL_BLEND_EQUATION_ALPHA) == GL_FUNC_ADD);
|
||||
CHECK(when, gl_float(GL_BLEND_COLOR, 0) == 0.0f);
|
||||
CHECK(when, gl_bool(GL_COLOR_WRITEMASK, 0) && gl_bool(GL_COLOR_WRITEMASK, 2));
|
||||
CHECK(when, gl_bool(GL_DEPTH_WRITEMASK, 0));
|
||||
CHECK(when, gl_int(GL_DEPTH_FUNC) == GL_LEQUAL);
|
||||
CHECK(when, fabsf(gl_float(GL_COLOR_CLEAR_VALUE, 0) - 0.25f) < 1e-6f);
|
||||
CHECK(when, fabsf(gl_float(GL_COLOR_CLEAR_VALUE, 1) - 0.5f) < 1e-6f);
|
||||
CHECK(when, fabsf(gl_float(GL_COLOR_CLEAR_VALUE, 2) - 0.75f) < 1e-6f);
|
||||
CHECK(when, gl_int(GL_CULL_FACE_MODE) == GL_BACK);
|
||||
CHECK(when, gl_int(GL_FRONT_FACE) == GL_CCW);
|
||||
CHECK(when, gl_int(GL_STENCIL_FUNC) == GL_ALWAYS);
|
||||
CHECK(when, gl_int(GL_STENCIL_FAIL) == GL_KEEP);
|
||||
CHECK(when, (gl_int(GL_STENCIL_WRITEMASK) & 0xff) == 0xff);
|
||||
CHECK(when, gl_float(GL_POLYGON_OFFSET_FACTOR, 0) == 0.0f);
|
||||
CHECK(when, gl_int(GL_VIEWPORT) == 0);
|
||||
CHECK(when, gl_int(GL_FRAMEBUFFER_BINDING) != 0); /* the core's target */
|
||||
}
|
||||
|
||||
static void check_defaults(void)
|
||||
{
|
||||
const char *when = "after the unbind";
|
||||
CHECK(when, !gl_enabled(GL_BLEND));
|
||||
CHECK(when, !gl_enabled(GL_DEPTH_TEST));
|
||||
CHECK(when, !gl_enabled(GL_CULL_FACE));
|
||||
CHECK(when, gl_int(GL_BLEND_SRC_RGB) == GL_ONE);
|
||||
CHECK(when, gl_int(GL_BLEND_DST_RGB) == GL_ZERO);
|
||||
CHECK(when, gl_int(GL_DEPTH_FUNC) == GL_LESS);
|
||||
CHECK(when, (gl_int(GL_STENCIL_WRITEMASK) & 0xff) == 0xff);
|
||||
CHECK(when, gl_int(GL_CURRENT_PROGRAM) == 0);
|
||||
}
|
||||
|
||||
#include <glsm/glsmsym.h>
|
||||
|
||||
static void core_state(void)
|
||||
{
|
||||
rglDisable(SGL_BLEND);
|
||||
rglDisable(SGL_SCISSOR_TEST);
|
||||
rglDisable(SGL_STENCIL_TEST);
|
||||
rglDisable(SGL_POLYGON_OFFSET_FILL);
|
||||
rglEnable(SGL_DEPTH_TEST);
|
||||
rglEnable(SGL_CULL_FACE);
|
||||
rglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
rglDepthFunc(GL_LEQUAL);
|
||||
rglClearColor(0.25f, 0.5f, 0.75f, 1.0f);
|
||||
rglViewport(0, 0, 64, 48);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const EGLint config_attrs[] = { EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
|
||||
#ifdef HAVE_OPENGLES
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||
#else
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
|
||||
#endif
|
||||
EGL_STENCIL_SIZE, 8, EGL_NONE };
|
||||
#ifdef HAVE_OPENGLES
|
||||
const EGLint context_attrs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
|
||||
#else
|
||||
const EGLint *context_attrs = NULL;
|
||||
#endif
|
||||
const EGLint surface_attrs[] = { EGL_WIDTH, 16, EGL_HEIGHT, 16, EGL_NONE };
|
||||
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
EGLConfig config;
|
||||
EGLint count;
|
||||
EGLContext context;
|
||||
EGLSurface surface;
|
||||
glsm_ctx_params_t params;
|
||||
GLuint tex[2], buf, again, offscreen;
|
||||
unsigned slice;
|
||||
|
||||
assert(eglInitialize(display, NULL, NULL));
|
||||
#ifdef HAVE_OPENGLES
|
||||
assert(eglBindAPI(EGL_OPENGL_ES_API));
|
||||
#else
|
||||
assert(eglBindAPI(EGL_OPENGL_API));
|
||||
#endif
|
||||
assert(eglChooseConfig(display, config_attrs, &config, 1, &count) && count);
|
||||
context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attrs);
|
||||
surface = eglCreatePbufferSurface(display, config, surface_attrs);
|
||||
assert(context != EGL_NO_CONTEXT && surface != EGL_NO_SURFACE);
|
||||
assert(eglMakeCurrent(display, surface, surface, context));
|
||||
rglgen_resolve_symbols(resolve);
|
||||
|
||||
glGenTextures(1, &frontend_tex);
|
||||
glBindTexture(GL_TEXTURE_2D, frontend_tex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 64, 48, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||
glGenFramebuffers(1, &frontend_fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, frontend_fbo);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, frontend_tex, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
params.environ_cb = environment;
|
||||
assert(glsm_ctl(GLSM_CTL_STATE_CONTEXT_INIT, ¶ms));
|
||||
assert(glsm_ctl(GLSM_CTL_STATE_CONTEXT_RESET, NULL));
|
||||
assert(glsm_ctl(GLSM_CTL_STATE_SETUP, NULL));
|
||||
glsm_draw_target(64, 48);
|
||||
assert(glsm_ctl(GLSM_CTL_STATE_BIND, NULL));
|
||||
core_state();
|
||||
|
||||
for (slice = 0; slice < 3; slice++)
|
||||
{
|
||||
assert(glsm_ctl(GLSM_CTL_STATE_UNBIND, NULL));
|
||||
check_defaults();
|
||||
frontend_draws();
|
||||
assert(glsm_ctl(GLSM_CTL_STATE_BIND, NULL));
|
||||
check_core_state("after the bind");
|
||||
/* what the core asks for again, cached, is what GL holds */
|
||||
core_state();
|
||||
check_core_state("after the core sets it again");
|
||||
}
|
||||
|
||||
/* the clear colour, set while another framebuffer is bound, is the
|
||||
* colour GL holds after */
|
||||
glGenFramebuffers(1, &offscreen);
|
||||
rglBindFramebuffer(GL_FRAMEBUFFER, offscreen);
|
||||
rglClearColor(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
rglBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
rglClearColor(0.25f, 0.5f, 0.75f, 1.0f);
|
||||
CHECK("clear colour", fabsf(gl_float(GL_COLOR_CLEAR_VALUE, 0) - 0.25f) < 1e-6f);
|
||||
|
||||
/* glBlendFunc after glBlendFuncSeparate */
|
||||
rglBlendFunc(GL_ONE, GL_ZERO);
|
||||
rglBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
|
||||
rglBlendFunc(GL_ONE, GL_ZERO);
|
||||
CHECK("blend func", gl_int(GL_BLEND_SRC_RGB) == GL_ONE && gl_int(GL_BLEND_DST_ALPHA) == GL_ZERO);
|
||||
|
||||
/* a texture deleted while another unit is active */
|
||||
rglGenTextures(2, tex);
|
||||
rglActiveTexture(GL_TEXTURE1);
|
||||
rglBindTexture(GL_TEXTURE_2D, tex[0]);
|
||||
rglActiveTexture(GL_TEXTURE0);
|
||||
rglDeleteTextures(1, &tex[0]);
|
||||
rglGenTextures(1, &again); /* GL gives the name again */
|
||||
rglActiveTexture(GL_TEXTURE1);
|
||||
rglBindTexture(GL_TEXTURE_2D, again);
|
||||
CHECK("texture name given again", (GLuint)gl_int(GL_TEXTURE_BINDING_2D) == again);
|
||||
rglActiveTexture(GL_TEXTURE0);
|
||||
|
||||
/* a deleted buffer */
|
||||
rglGenBuffers(1, &buf);
|
||||
rglBindBuffer(GL_ARRAY_BUFFER, buf);
|
||||
rglDeleteBuffers(1, &buf);
|
||||
rglGenBuffers(1, &again);
|
||||
rglBindBuffer(GL_ARRAY_BUFFER, again);
|
||||
CHECK("buffer name given again", (GLuint)gl_int(GL_ARRAY_BUFFER_BINDING) == again);
|
||||
|
||||
/* a deleted framebuffer that was to be bound */
|
||||
rglBindFramebuffer(GL_FRAMEBUFFER, offscreen);
|
||||
rglDeleteFramebuffers(1, &offscreen);
|
||||
rglClear(GL_COLOR_BUFFER_BIT);
|
||||
CHECK("deleted framebuffer", gl_int(GL_FRAMEBUFFER_BINDING) != 0 && glGetError() == GL_NO_ERROR);
|
||||
|
||||
#ifndef HAVE_OPENGLES
|
||||
{
|
||||
/* the element buffer belongs to the vertex array */
|
||||
GLuint vao[2], ebo;
|
||||
rglGenVertexArrays(2, vao);
|
||||
rglGenBuffers(1, &ebo);
|
||||
rglBindVertexArray(vao[0]);
|
||||
rglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
rglBindVertexArray(vao[1]);
|
||||
rglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
CHECK("element buffer of a vertex array", (GLuint)gl_int(GL_ELEMENT_ARRAY_BUFFER_BINDING) == ebo);
|
||||
rglBindVertexArray(0);
|
||||
}
|
||||
{
|
||||
/* the draw buffers asked for, each time */
|
||||
GLenum none = GL_NONE, colour = GL_COLOR_ATTACHMENT0;
|
||||
GLuint fbo, ctex;
|
||||
rglGenFramebuffers(1, &fbo);
|
||||
rglGenTextures(1, &ctex);
|
||||
rglBindTexture(GL_TEXTURE_2D, ctex);
|
||||
rglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||
rglBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
rglFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ctex, 0);
|
||||
rglDrawBuffers(1, &none);
|
||||
rglDrawBuffers(1, &colour);
|
||||
CHECK("draw buffers", gl_int(GL_DRAW_BUFFER0) == GL_COLOR_ATTACHMENT0);
|
||||
rglBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
#endif
|
||||
CHECK("no GL error", glGetError() == GL_NO_ERROR);
|
||||
|
||||
if (failures)
|
||||
{
|
||||
fprintf(stderr, "test_glsm_state: %d failures\n", failures);
|
||||
return 1;
|
||||
}
|
||||
puts("test_glsm_state: GL holds the core's state");
|
||||
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
eglDestroySurface(display, surface);
|
||||
eglDestroyContext(display, context);
|
||||
eglTerminate(display);
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user