diff --git a/direct/src/directtools/DirectUtil.py b/direct/src/directtools/DirectUtil.py index 9919f841b2..2a244e325c 100644 --- a/direct/src/directtools/DirectUtil.py +++ b/direct/src/directtools/DirectUtil.py @@ -1,5 +1,7 @@ from .DirectGlobals import * +from panda3d.core import VBase4 +from direct.task.Task import Task # Routines to adjust values def ROUND_TO(value, divisor): diff --git a/direct/src/gui/DirectScrolledFrame.py b/direct/src/gui/DirectScrolledFrame.py index d5274de6fe..2c60ed950f 100644 --- a/direct/src/gui/DirectScrolledFrame.py +++ b/direct/src/gui/DirectScrolledFrame.py @@ -33,7 +33,7 @@ class DirectScrolledFrame(DirectFrame): ('canvasSize', (-1, 1, -1, 1), self.setCanvasSize), ('manageScrollBars', 1, self.setManageScrollBars), ('autoHideScrollBars', 1, self.setAutoHideScrollBars), - ('scrollBarWidth', 0.08, None), + ('scrollBarWidth', 0.08, self.setScrollBarWidth), ('borderWidth', (0.01, 0.01), self.setBorderWidth), ) @@ -72,6 +72,11 @@ class DirectScrolledFrame(DirectFrame): # Call option initialization functions self.initialiseoptions(DirectScrolledFrame) + def setScrollBarWidth(self): + w = self['scrollBarWidth'] + self.verticalScroll["frameSize"] = (-w / 2.0, w / 2.0, -1, 1) + self.horizontalScroll["frameSize"] = (-1, 1, -w / 2.0, w / 2.0) + def setCanvasSize(self): f = self['canvasSize'] self.guiItem.setVirtualFrame(f[0], f[1], f[2], f[3]) diff --git a/dtool/src/prc/notify.cxx b/dtool/src/prc/notify.cxx index f9c39135e8..690910ef4a 100644 --- a/dtool/src/prc/notify.cxx +++ b/dtool/src/prc/notify.cxx @@ -19,6 +19,7 @@ #include "filename.h" #include "config_prc.h" +#include #include #ifdef BUILD_IPHONE @@ -422,28 +423,29 @@ string_severity(const string &str) { */ void Notify:: config_initialized() { - static bool already_initialized = false; - if (already_initialized) { - nout << "Notify::config_initialized() called more than once.\n"; - return; - } - already_initialized = true; + // We allow this to be called more than once to allow the user to specify a + // notify-output even after the initial import of Panda3D modules. However, + // it cannot be changed after the first time it is set. if (_ostream_ptr == &cerr) { - ConfigVariableFilename notify_output + static ConfigVariableFilename notify_output ("notify-output", "", "The filename to which to write all the output of notify"); - if (!notify_output.empty()) { - if (notify_output == "stdout") { + // We use this to ensure that only one thread can initialize the output. + static std::atomic_flag initialized = ATOMIC_FLAG_INIT; + + std::string value = notify_output.get_value(); + if (!value.empty() && !initialized.test_and_set()) { + if (value == "stdout") { cout.setf(std::ios::unitbuf); set_ostream_ptr(&cout, false); - } else if (notify_output == "stderr") { + } else if (value == "stderr") { set_ostream_ptr(&cerr, false); } else { - Filename filename = notify_output; + Filename filename = value; filename.set_text(); #ifdef BUILD_IPHONE // On the iPhone, route everything through cerr, and then send cerr to diff --git a/dtool/src/prc/notifyCategory.cxx b/dtool/src/prc/notifyCategory.cxx index 3d856ceb08..86bf63f736 100644 --- a/dtool/src/prc/notifyCategory.cxx +++ b/dtool/src/prc/notifyCategory.cxx @@ -181,10 +181,16 @@ update_severity_cache() { } else { // Unless, of course, we're the root. _severity_cache = NS_info; + + // Take this opportunity to have Notify check whether the notify-output + // variable changed. + Notify::ptr()->config_initialized(); } } else { _severity_cache = _severity; + Notify::ptr()->config_initialized(); } + mark_cache_valid(_local_modified); } diff --git a/panda/src/audiotraits/openalAudioManager.cxx b/panda/src/audiotraits/openalAudioManager.cxx index 76e7b0dd81..ea03b0c0c7 100644 --- a/panda/src/audiotraits/openalAudioManager.cxx +++ b/panda/src/audiotraits/openalAudioManager.cxx @@ -252,7 +252,9 @@ select_audio_device() { devices = (const char *)alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER); if (devices) { - audio_cat.debug() << "All OpenAL devices:\n"; + if (audio_cat.is_debug()) { + audio_cat.debug() << "All OpenAL devices:\n"; + } while (*devices) { string device(devices); @@ -280,7 +282,9 @@ select_audio_device() { devices = (const char *)alcGetString(nullptr, ALC_DEVICE_SPECIFIER); if (devices) { - audio_cat.debug() << "OpenAL drivers:\n"; + if (audio_cat.is_debug()) { + audio_cat.debug() << "OpenAL drivers:\n"; + } while (*devices) { string device(devices); diff --git a/panda/src/glstuff/glGraphicsBuffer_src.cxx b/panda/src/glstuff/glGraphicsBuffer_src.cxx index b85a3e76c1..d5252944f5 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.cxx +++ b/panda/src/glstuff/glGraphicsBuffer_src.cxx @@ -778,7 +778,9 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, } if (attachpoint == GL_DEPTH_ATTACHMENT_EXT) { - GLCAT.debug() << "Binding texture " << *tex << " to depth attachment.\n"; + if (GLCAT.is_debug()) { + GLCAT.debug() << "Binding texture " << *tex << " to depth attachment.\n"; + } attach_tex(layer, 0, tex, GL_DEPTH_ATTACHMENT_EXT); @@ -789,7 +791,9 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, #endif if (slot == RTP_depth_stencil) { - GLCAT.debug() << "Binding texture " << *tex << " to stencil attachment.\n"; + if (GLCAT.is_debug()) { + GLCAT.debug() << "Binding texture " << *tex << " to stencil attachment.\n"; + } attach_tex(layer, 0, tex, GL_STENCIL_ATTACHMENT_EXT); @@ -801,7 +805,9 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, } } else { - GLCAT.debug() << "Binding texture " << *tex << " to color attachment.\n"; + if (GLCAT.is_debug()) { + GLCAT.debug() << "Binding texture " << *tex << " to color attachment.\n"; + } attach_tex(layer, 0, tex, attachpoint); @@ -988,7 +994,9 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, _rb[slot]); if (slot == RTP_depth_stencil) { - GLCAT.debug() << "Creating depth stencil renderbuffer.\n"; + if (GLCAT.is_debug()) { + GLCAT.debug() << "Creating depth stencil renderbuffer.\n"; + } // Allocate renderbuffer storage for depth stencil. GLint depth_size = 0, stencil_size = 0; glgsg->_glRenderbufferStorage(GL_RENDERBUFFER_EXT, gl_format, _rb_size_x, _rb_size_y); @@ -1014,7 +1022,9 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, report_my_gl_errors(); } else if (slot == RTP_depth) { - GLCAT.debug() << "Creating depth renderbuffer.\n"; + if (GLCAT.is_debug()) { + GLCAT.debug() << "Creating depth renderbuffer.\n"; + } // Allocate renderbuffer storage for regular depth. GLint depth_size = 0; glgsg->_glRenderbufferStorage(GL_RENDERBUFFER_EXT, gl_format, _rb_size_x, _rb_size_y); @@ -1052,7 +1062,9 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, report_my_gl_errors(); } else { - GLCAT.debug() << "Creating color renderbuffer.\n"; + if (GLCAT.is_debug()) { + GLCAT.debug() << "Creating color renderbuffer.\n"; + } glgsg->_glRenderbufferStorage(GL_RENDERBUFFER_EXT, gl_format, _rb_size_x, _rb_size_y); GLint red_size = 0, green_size = 0, blue_size = 0, alpha_size = 0; diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 3ad8e01428..c913d1ff34 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -722,10 +722,12 @@ reset() { || has_extension("GL_KHR_debug") || has_extension("GL_ARB_debug_output"); - if (_supports_debug) { - GLCAT.debug() << "gl-debug supported, but NOT enabled.\n"; - } else { - GLCAT.debug() << "gl-debug disabled and unsupported.\n"; + if (GLCAT.is_debug()) { + if (_supports_debug) { + GLCAT.debug() << "gl-debug supported, but NOT enabled.\n"; + } else { + GLCAT.debug() << "gl-debug disabled and unsupported.\n"; + } } } diff --git a/panda/src/glstuff/glShaderContext_src.cxx b/panda/src/glstuff/glShaderContext_src.cxx index 2c3fadabe2..7c621f69ba 100644 --- a/panda/src/glstuff/glShaderContext_src.cxx +++ b/panda/src/glstuff/glShaderContext_src.cxx @@ -3306,7 +3306,7 @@ glsl_compile_and_link() { } _glgsg->report_my_gl_errors(); - return true; + return valid; } #endif // OPENGLES_1 diff --git a/panda/src/gobj/shader.cxx b/panda/src/gobj/shader.cxx index d42e6a4734..a60a7e169b 100644 --- a/panda/src/gobj/shader.cxx +++ b/panda/src/gobj/shader.cxx @@ -3274,8 +3274,10 @@ load(const Filename &file, ShaderLanguage lang) { shader_cat.info() << "Shader " << file << " was modified on disk, reloading.\n"; } else { - shader_cat.debug() - << "Shader " << file << " was found in shader cache.\n"; + if (shader_cat.is_debug()) { + shader_cat.debug() + << "Shader " << file << " was found in shader cache.\n"; + } return i->second; } } @@ -3312,8 +3314,10 @@ load(ShaderLanguage lang, const Filename &vertex, shader_cat.info() << "Shader was modified on disk, reloading.\n"; } else { - shader_cat.debug() - << "Shader was found in shader cache.\n"; + if (shader_cat.is_debug()) { + shader_cat.debug() + << "Shader was found in shader cache.\n"; + } return i->second; } } @@ -3365,8 +3369,10 @@ load_compute(ShaderLanguage lang, const Filename &fn) { shader_cat.info() << "Compute shader " << fn << " was modified on disk, reloading.\n"; } else { - shader_cat.debug() - << "Compute shader " << fn << " was found in shader cache.\n"; + if (shader_cat.is_debug()) { + shader_cat.debug() + << "Compute shader " << fn << " was found in shader cache.\n"; + } return i->second; } } diff --git a/panda/src/movies/movieTypeRegistry.cxx b/panda/src/movies/movieTypeRegistry.cxx index dca0ef9400..64acc8e42a 100644 --- a/panda/src/movies/movieTypeRegistry.cxx +++ b/panda/src/movies/movieTypeRegistry.cxx @@ -90,7 +90,7 @@ register_audio_type(MakeAudioFunc func, const string &extensions) { if (_audio_type_registry.count(*wi)) { movies_cat->warning() << "Attempt to register multiple audio types with extension " << (*wi) << "\n"; - } else { + } else if (movies_cat->is_debug()) { movies_cat->debug() << "Registered audio type with extension " << (*wi) << "\n"; } @@ -219,7 +219,7 @@ register_video_type(MakeVideoFunc func, const string &extensions) { if (_video_type_registry.count(*wi)) { movies_cat->warning() << "Attempt to register multiple video types with extension " << (*wi) << "\n"; - } else { + } else if (movies_cat->is_debug()) { movies_cat->debug() << "Registered video type with extension " << (*wi) << "\n"; } diff --git a/panda/src/pnmimagetypes/pnmFileTypePNG.cxx b/panda/src/pnmimagetypes/pnmFileTypePNG.cxx index 7d64faed50..e4b144dd95 100644 --- a/panda/src/pnmimagetypes/pnmFileTypePNG.cxx +++ b/panda/src/pnmimagetypes/pnmFileTypePNG.cxx @@ -227,10 +227,12 @@ Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) : } } - pnmimage_png_cat.debug() - << "width = " << width << " height = " << height << " bit_depth = " - << bit_depth << " color_type = " << color_type - << " color_space = " << _color_space << "\n"; + if (pnmimage_png_cat.is_debug()) { + pnmimage_png_cat.debug() + << "width = " << width << " height = " << height << " bit_depth = " + << bit_depth << " color_type = " << color_type + << " color_space = " << _color_space << "\n"; + } _x_size = width; _y_size = height; @@ -242,32 +244,42 @@ Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) : switch (color_type) { case PNG_COLOR_TYPE_GRAY: - pnmimage_png_cat.debug() - << "PNG_COLOR_TYPE_GRAY\n"; + if (pnmimage_png_cat.is_debug()) { + pnmimage_png_cat.debug() + << "PNG_COLOR_TYPE_GRAY\n"; + } _num_channels = 1; break; case PNG_COLOR_TYPE_GRAY_ALPHA: - pnmimage_png_cat.debug() - << "PNG_COLOR_TYPE_GRAY_ALPHA\n"; + if (pnmimage_png_cat.is_debug()) { + pnmimage_png_cat.debug() + << "PNG_COLOR_TYPE_GRAY_ALPHA\n"; + } _num_channels = 2; break; case PNG_COLOR_TYPE_RGB: - pnmimage_png_cat.debug() - << "PNG_COLOR_TYPE_RGB\n"; + if (pnmimage_png_cat.is_debug()) { + pnmimage_png_cat.debug() + << "PNG_COLOR_TYPE_RGB\n"; + } _num_channels = 3; break; case PNG_COLOR_TYPE_RGB_ALPHA: - pnmimage_png_cat.debug() - << "PNG_COLOR_TYPE_RGB_ALPHA\n"; + if (pnmimage_png_cat.is_debug()) { + pnmimage_png_cat.debug() + << "PNG_COLOR_TYPE_RGB_ALPHA\n"; + } _num_channels = 4; break; case PNG_COLOR_TYPE_PALETTE: - pnmimage_png_cat.debug() - << "PNG_COLOR_TYPE_PALETTE\n"; + if (pnmimage_png_cat.is_debug()) { + pnmimage_png_cat.debug() + << "PNG_COLOR_TYPE_PALETTE\n"; + } png_set_palette_to_rgb(_png); _maxval = 255; _num_channels = 3; @@ -566,8 +578,10 @@ write_data(xel *array, xelval *alpha_data) { if (png_palette) { if (png_bit_depth <= 8) { if (compute_palette(palette, array, alpha_data, png_max_palette)) { - pnmimage_png_cat.debug() - << palette.size() << " colors found.\n"; + if (pnmimage_png_cat.is_debug()) { + pnmimage_png_cat.debug() + << palette.size() << " colors found.\n"; + } int palette_bit_depth = make_png_bit_depth(pm_maxvaltobits(palette.size() - 1)); @@ -581,10 +595,12 @@ write_data(xel *array, xelval *alpha_data) { if (palette_bit_depth < total_bits || _maxval != (1 << true_bit_depth) - 1) { - pnmimage_png_cat.debug() - << "palette bit depth of " << palette_bit_depth - << " improves on bit depth of " << total_bits - << "; making a palette image.\n"; + if (pnmimage_png_cat.is_debug()) { + pnmimage_png_cat.debug() + << "palette bit depth of " << palette_bit_depth + << " improves on bit depth of " << total_bits + << "; making a palette image.\n"; + } color_type = PNG_COLOR_TYPE_PALETTE; @@ -611,36 +627,40 @@ write_data(xel *array, xelval *alpha_data) { png_set_PLTE(_png, _info, png_palette_table, palette.size()); if (has_alpha()) { - pnmimage_png_cat.debug() - << "palette contains " << num_alpha << " transparent entries.\n"; + if (pnmimage_png_cat.is_debug()) { + pnmimage_png_cat.debug() + << "palette contains " << num_alpha << " transparent entries.\n"; + } png_set_tRNS(_png, _info, png_trans, num_alpha, nullptr); } - } else { + } else if (pnmimage_png_cat.is_debug()) { pnmimage_png_cat.debug() << "palette bit depth of " << palette_bit_depth << " does not improve on bit depth of " << total_bits << "; not making a palette image.\n"; } - } else { + } else if (pnmimage_png_cat.is_debug()) { pnmimage_png_cat.debug() << "more than " << png_max_palette << " colors found; not making a palette image.\n"; } - } else { + } else if (pnmimage_png_cat.is_debug()) { pnmimage_png_cat.debug() << "maxval exceeds 255; not making a palette image.\n"; } - } else { + } else if (pnmimage_png_cat.is_debug()) { pnmimage_png_cat.debug() << "palette images are not enabled.\n"; } - pnmimage_png_cat.debug() - << "width = " << _x_size << " height = " << _y_size - << " maxval = " << _maxval << " bit_depth = " - << png_bit_depth << " color_type = " << color_type - << " color_space = " << _color_space << "\n"; + if (pnmimage_png_cat.is_debug()) { + pnmimage_png_cat.debug() + << "width = " << _x_size << " height = " << _y_size + << " maxval = " << _maxval << " bit_depth = " + << png_bit_depth << " color_type = " << color_type + << " color_space = " << _color_space << "\n"; + } png_set_IHDR(_png, _info, _x_size, _y_size, png_bit_depth, color_type, PNG_INTERLACE_NONE, diff --git a/tests/display/cg_bad.sha b/tests/display/cg_bad.sha new file mode 100644 index 0000000000..7f3a77eda4 --- /dev/null +++ b/tests/display/cg_bad.sha @@ -0,0 +1,21 @@ +//Cg +// + +void vshader(float4 vtx_position : POSITION, + float2 vtx_texcoord0 : TEXCOORD0, + uniform float4x4 mat_modelproj, + out float4 l_position : POSITION, + out float2 l_texcoord0 : TEXCOORD0) +{ + l_position = mul(mat_modelproj, vtx_position); + l_texcoord0 = vtx_texcoord0; +} + +void fshader(float2 l_texcoord0 : TEXCOORD0, + uniform sampler2D tex_0 : TEXUNIT0, + out float4 o_color : COLOR) +{ + float4 texColor = tex2D(tex_0, l_texcoord0); + does_not_exist = texColor * 2 * (texColor.w - 0.5); +} + diff --git a/tests/display/cg_simple.sha b/tests/display/cg_simple.sha new file mode 100644 index 0000000000..64e419fd0e --- /dev/null +++ b/tests/display/cg_simple.sha @@ -0,0 +1,21 @@ +//Cg +// + +void vshader(float4 vtx_position : POSITION, + float2 vtx_texcoord0 : TEXCOORD0, + uniform float4x4 mat_modelproj, + out float4 l_position : POSITION, + out float2 l_texcoord0 : TEXCOORD0) +{ + l_position = mul(mat_modelproj, vtx_position); + l_texcoord0 = vtx_texcoord0; +} + +void fshader(float2 l_texcoord0 : TEXCOORD0, + uniform sampler2D tex_0 : TEXUNIT0, + out float4 o_color : COLOR) +{ + float4 texColor = tex2D(tex_0, l_texcoord0); + o_color = texColor * 2 * (texColor.w - 0.5); +} + diff --git a/tests/display/glsl_bad.vert b/tests/display/glsl_bad.vert new file mode 100644 index 0000000000..8a324916ec --- /dev/null +++ b/tests/display/glsl_bad.vert @@ -0,0 +1,12 @@ +#version 130 + +// Uniform inputs +uniform mat4 p3d_ModelViewProjectionMatrix; + +// Vertex inputs +in vec4 p3d_Vertex; + +void main() { + gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex; + does_not_exist = p3d_Vertex; +} diff --git a/tests/display/glsl_include.vert b/tests/display/glsl_include.vert new file mode 100644 index 0000000000..1826fdf677 --- /dev/null +++ b/tests/display/glsl_include.vert @@ -0,0 +1,7 @@ +#version 130 + +#pragma include "glsl_include_inputs.vert" + +void main() { + gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex; +} diff --git a/tests/display/glsl_include_inputs.vert b/tests/display/glsl_include_inputs.vert new file mode 100644 index 0000000000..125078ce16 --- /dev/null +++ b/tests/display/glsl_include_inputs.vert @@ -0,0 +1,5 @@ +// Uniform inputs +uniform mat4 p3d_ModelViewProjectionMatrix; + +// Vertex inputs +in vec4 p3d_Vertex; diff --git a/tests/display/glsl_simple.frag b/tests/display/glsl_simple.frag new file mode 100644 index 0000000000..5f190e015c --- /dev/null +++ b/tests/display/glsl_simple.frag @@ -0,0 +1,5 @@ +#version 130 + +void main() { + gl_FragColor = vec4(0, 0, 0, 1); +} diff --git a/tests/display/glsl_simple.vert b/tests/display/glsl_simple.vert new file mode 100644 index 0000000000..ea3afd7498 --- /dev/null +++ b/tests/display/glsl_simple.vert @@ -0,0 +1,11 @@ +#version 130 + +// Uniform inputs +uniform mat4 p3d_ModelViewProjectionMatrix; + +// Vertex inputs +in vec4 p3d_Vertex; + +void main() { + gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex; +} diff --git a/tests/display/test_cg_shader.py b/tests/display/test_cg_shader.py new file mode 100644 index 0000000000..8a6c4d7a74 --- /dev/null +++ b/tests/display/test_cg_shader.py @@ -0,0 +1,28 @@ +import os + +from panda3d import core + + +SHADERS_DIR = core.Filename.from_os_specific(os.path.dirname(__file__)) + + +def run_cg_compile_check(gsg, shader_path, expect_fail=False): + """Compile supplied Cg shader path and check for errors""" + shader = core.Shader.load(shader_path, core.Shader.SL_Cg) + # assert shader.is_prepared(gsg.prepared_objects) + if expect_fail: + assert shader is None + else: + assert shader is not None + + +def test_cg_compile_error(gsg): + """Test getting compile errors from bad Cg shaders""" + shader_path = core.Filename(SHADERS_DIR, 'cg_bad.sha') + run_cg_compile_check(gsg, shader_path, expect_fail=True) + + +def test_cg_from_file(gsg): + """Test compiling Cg shaders from files""" + shader_path = core.Filename(SHADERS_DIR, 'cg_simple.sha') + run_cg_compile_check(gsg, shader_path) diff --git a/tests/display/test_glsl_shader.py b/tests/display/test_glsl_shader.py index 401166a26d..a380f8ccfb 100644 --- a/tests/display/test_glsl_shader.py +++ b/tests/display/test_glsl_shader.py @@ -1,9 +1,13 @@ from panda3d import core +import os import struct import pytest from _pytest.outcomes import Failed +SHADERS_DIR = core.Filename.from_os_specific(os.path.dirname(__file__)) + + # This is the template for the compute shader that is used by run_glsl_test. # It defines an assert() macro that writes failures to a buffer, indexed by # line number. @@ -102,6 +106,19 @@ def run_glsl_test(gsg, body, preamble="", inputs={}, version=150, exts=set()): pytest.fail("{0} GLSL assertions triggered:\n{1}".format(count, formatted)) +def run_glsl_compile_check(gsg, vert_path, frag_path, expect_fail=False): + """Compile supplied GLSL shader paths and check for errors""" + shader = core.Shader.load(core.Shader.SL_GLSL, vert_path, frag_path) + assert shader is not None + + shader.prepare_now(gsg.prepared_objects, gsg) + assert shader.is_prepared(gsg.prepared_objects) + if expect_fail: + assert shader.get_error_flag() + else: + assert not shader.get_error_flag() + + def test_glsl_test(gsg): "Test to make sure that the GLSL tests work correctly." @@ -359,3 +376,24 @@ def test_glsl_write_extract_image_buffer(gsg): assert struct.unpack('I', tex1.get_ram_image()) == (123,) assert struct.unpack('i', tex2.get_ram_image()) == (-456,) + + +def test_glsl_compile_error(gsg): + """Test getting compile errors from bad shaders""" + vert_path = core.Filename(SHADERS_DIR, 'glsl_bad.vert') + frag_path = core.Filename(SHADERS_DIR, 'glsl_simple.frag') + run_glsl_compile_check(gsg, vert_path, frag_path, expect_fail=True) + + +def test_glsl_from_file(gsg): + """Test compiling GLSL shaders from files""" + vert_path = core.Filename(SHADERS_DIR, 'glsl_simple.vert') + frag_path = core.Filename(SHADERS_DIR, 'glsl_simple.frag') + run_glsl_compile_check(gsg, vert_path, frag_path) + + +def test_glsl_includes(gsg): + """Test preprocessing includes in GLSL shaders""" + vert_path = core.Filename(SHADERS_DIR, 'glsl_include.vert') + frag_path = core.Filename(SHADERS_DIR, 'glsl_simple.frag') + run_glsl_compile_check(gsg, vert_path, frag_path)