Scene*: Add ::supported() and change ::setup() to return bool.
diff --git a/src/scene-buffer.cpp b/src/scene-buffer.cpp
index 5cf11a6..ef3a0b9 100644
--- a/src/scene-buffer.cpp
+++ b/src/scene-buffer.cpp
@@ -327,6 +327,22 @@
 }
 
 bool
+SceneBuffer::supported(bool show_errors)
+{
+    if (options_["update-method"].value != "subdata" &&
+        (GLExtensions::MapBuffer == 0 || GLExtensions::UnmapBuffer == 0))
+    {
+        if (show_errors) {
+            Log::error("Requested MapBuffer VBO update method but GL_OES_mapbuffer"
+                       " is not supported!\n");
+        }
+        return false;
+    }
+
+    return true;
+}
+
+bool
 SceneBuffer::load()
 {
     running_ = false;
@@ -339,14 +355,14 @@
 {
 }
 
-void
+bool
 SceneBuffer::setup()
 {
     using LibMatrix::vec3;
 
-    Scene::setup();
+    if (!Scene::setup())
+        return false;
 
-    bool should_run = true;
     bool interleave = (options_["interleave"].value == "true");
     Mesh::VBOUpdateMethod update_method;
     Mesh::VBOUsage usage;
@@ -374,13 +390,6 @@
     nlength = Util::fromString<size_t>(options_["columns"].value);
     nwidth = Util::fromString<size_t>(options_["rows"].value);
 
-    if (update_method == Mesh::VBOUpdateMethodMap &&
-        (GLExtensions::MapBuffer == 0 || GLExtensions::UnmapBuffer == 0))
-    {
-        Log::error("Requested MapBuffer VBO update method but GL_OES_mapbuffer"
-                   " is not supported!\n");
-        should_run = false;
-    }
 
     priv_->wave = new WaveMesh(5.0, 2.0, nlength, nwidth,
                                update_fraction * (1.0 - update_dispersion + 0.0001),
@@ -397,9 +406,11 @@
     glDisable(GL_CULL_FACE);
 
     currentFrame_ = 0;
-    running_ = should_run;
+    running_ = true;
     startTime_ = Util::get_timestamp_us() / 1000000.0;
     lastUpdateTime_ = startTime_;
+
+    return true;
 }
 
 void
diff --git a/src/scene-build.cpp b/src/scene-build.cpp
index 39a5ae5..22c948a 100644
--- a/src/scene-build.cpp
+++ b/src/scene-build.cpp
@@ -80,12 +80,13 @@
     mesh_.reset();
 }
 
-void
+bool
 SceneBuild::setup()
 {
     using LibMatrix::vec3;
 
-    Scene::setup();
+    if (!Scene::setup())
+        return false;
 
     /* Set up shaders */
     static const std::string vtx_shader_filename(GLMARK_DATA_PATH"/shaders/light-basic.vert");
@@ -102,7 +103,7 @@
     if (!Scene::load_shaders_from_strings(program_, vtx_source.str(),
                                           frg_source.str()))
     {
-        return;
+        return false;
     }
 
     Model model;
@@ -110,7 +111,7 @@
     bool modelLoaded = model.load(whichModel);
 
     if(!modelLoaded)
-        return;
+        return false;
 
     // Now that we're successfully loaded, there are a few quirks about
     // some of the known models that we need to account for.  The draw
@@ -187,6 +188,8 @@
     running_ = true;
     startTime_ = Util::get_timestamp_us() / 1000000.0;
     lastUpdateTime_ = startTime_;
+
+    return true;
 }
 
 void
diff --git a/src/scene-bump.cpp b/src/scene-bump.cpp
index 6512684..2b6c01f 100644
--- a/src/scene-bump.cpp
+++ b/src/scene-bump.cpp
@@ -58,7 +58,7 @@
 {
 }
 
-void
+bool
 SceneBump::setup_model_plain(const std::string &type)
 {
     static const std::string vtx_shader_filename(GLMARK_DATA_PATH"/shaders/bump-poly.vert");
@@ -78,7 +78,7 @@
                                 high_poly_filename : low_poly_filename;
 
     if(!model.load(poly_filename))
-        return;
+        return false;
 
     model.calculate_normals();
 
@@ -100,16 +100,18 @@
     if (!Scene::load_shaders_from_strings(program_, vtx_source.str(),
                                           frg_source.str()))
     {
-        return;
+        return false;
     }
 
     std::vector<GLint> attrib_locations;
     attrib_locations.push_back(program_["position"].location());
     attrib_locations.push_back(program_["normal"].location());
     mesh_.set_attrib_locations(attrib_locations);
+
+    return true;
 }
 
-void
+bool
 SceneBump::setup_model_normals()
 {
     static const std::string vtx_shader_filename(GLMARK_DATA_PATH"/shaders/bump-normals.vert");
@@ -118,7 +120,7 @@
     Model model;
 
     if(!model.load("asteroid-low"))
-        return;
+        return false;
 
     /* Calculate the half vector */
     LibMatrix::vec3 halfVector(lightPosition.x(), lightPosition.y(), lightPosition.z());
@@ -147,7 +149,7 @@
     if (!Scene::load_shaders_from_strings(program_, vtx_source.str(),
                                           frg_source.str()))
     {
-        return;
+        return false;
     }
 
     std::vector<GLint> attrib_locations;
@@ -155,11 +157,16 @@
     attrib_locations.push_back(program_["texcoord"].location());
     mesh_.set_attrib_locations(attrib_locations);
 
-    Texture::load("asteroid-normal-map", &texture_,
-                  GL_NEAREST, GL_NEAREST, 0);
+    if (!Texture::load("asteroid-normal-map", &texture_,
+                       GL_NEAREST, GL_NEAREST, 0))
+    {
+        return false;
+    }
+
+    return true;
 }
 
-void
+bool
 SceneBump::setup_model_normals_tangent()
 {
     static const std::string vtx_shader_filename(GLMARK_DATA_PATH"/shaders/bump-normals-tangent.vert");
@@ -168,7 +175,7 @@
     Model model;
 
     if(!model.load("asteroid-low"))
-        return;
+        return false;
 
     model.calculate_normals();
 
@@ -197,7 +204,7 @@
     if (!Scene::load_shaders_from_strings(program_, vtx_source.str(),
                                           frg_source.str()))
     {
-        return;
+        return false;
     }
 
     std::vector<GLint> attrib_locations;
@@ -207,11 +214,16 @@
     attrib_locations.push_back(program_["tangent"].location());
     mesh_.set_attrib_locations(attrib_locations);
 
-    Texture::load("asteroid-normal-map-tangent", &texture_,
-                  GL_NEAREST, GL_NEAREST, 0);
+    if (!Texture::load("asteroid-normal-map-tangent", &texture_,
+                       GL_NEAREST, GL_NEAREST, 0))
+    {
+        return false;
+    }
+
+    return true;
 }
 
-void
+bool
 SceneBump::setup_model_height()
 {
     static const std::string vtx_shader_filename(GLMARK_DATA_PATH"/shaders/bump-height.vert");
@@ -220,7 +232,7 @@
     Model model;
 
     if(!model.load("asteroid-low"))
-        return;
+        return false;
 
     model.calculate_normals();
 
@@ -251,7 +263,7 @@
     if (!Scene::load_shaders_from_strings(program_, vtx_source.str(),
                                           frg_source.str()))
     {
-        return;
+        return false;
     }
 
     std::vector<GLint> attrib_locations;
@@ -261,27 +273,38 @@
     attrib_locations.push_back(program_["tangent"].location());
     mesh_.set_attrib_locations(attrib_locations);
 
-    Texture::load("asteroid-height-map", &texture_,
-                  GL_NEAREST, GL_NEAREST, 0);
+    if (!Texture::load("asteroid-height-map", &texture_,
+                       GL_NEAREST, GL_NEAREST, 0))
+    {
+        return false;
+    }
+
+    return true;
 }
 
-void
+bool
 SceneBump::setup()
 {
-    Scene::setup();
+    if (!Scene::setup())
+        return false;
 
     const std::string &bump_render = options_["bump-render"].value;
     Texture::find_textures();
     Model::find_models();
-    if (bump_render == "normals")
-        setup_model_normals();
-    else if (bump_render == "normals-tangent")
-        setup_model_normals_tangent();
-    else if (bump_render == "height")
-        setup_model_height();
-    else if (bump_render == "off" || bump_render == "high-poly")
-        setup_model_plain(bump_render);
 
+    bool setup_succeeded = false;
+
+    if (bump_render == "normals")
+        setup_succeeded = setup_model_normals();
+    else if (bump_render == "normals-tangent")
+        setup_succeeded = setup_model_normals_tangent();
+    else if (bump_render == "height")
+        setup_succeeded = setup_model_height();
+    else if (bump_render == "off" || bump_render == "high-poly")
+        setup_succeeded = setup_model_plain(bump_render);
+
+    if (!setup_succeeded)
+        return false;
 
     mesh_.build_vbo();
 
@@ -296,6 +319,8 @@
     running_ = true;
     startTime_ = Util::get_timestamp_us() / 1000000.0;
     lastUpdateTime_ = startTime_;
+
+    return true;
 }
 
 void
diff --git a/src/scene-conditionals.cpp b/src/scene-conditionals.cpp
index 61367a8..778cb33 100644
--- a/src/scene-conditionals.cpp
+++ b/src/scene-conditionals.cpp
@@ -89,10 +89,11 @@
     return source.str();
 }
 
-void
+bool
 SceneConditionals::setup()
 {
-    SceneGrid::setup();
+    if (!SceneGrid::setup())
+        return false;
 
     /* Parse options */
     bool vtx_conditionals = options_["vertex-conditionals"].value == "true";
@@ -104,7 +105,7 @@
     std::string frg_shader(get_fragment_shader_source(frg_steps, frg_conditionals));
 
     if (!Scene::load_shaders_from_strings(program_, vtx_shader, frg_shader))
-        return;
+        return false;
 
     program_.start();
 
@@ -115,6 +116,8 @@
     running_ = true;
     startTime_ = Util::get_timestamp_us() / 1000000.0;
     lastUpdateTime_ = startTime_;
+
+    return true;
 }
 
 Scene::ValidationResult
diff --git a/src/scene-default-options.cpp b/src/scene-default-options.cpp
index 78a7ac5..0cc8c89 100644
--- a/src/scene-default-options.cpp
+++ b/src/scene-default-options.cpp
@@ -23,7 +23,7 @@
 #include "benchmark.h"
 #include "log.h"
 
-void
+bool
 SceneDefaultOptions::setup()
 {
     const std::map<std::string, Scene *> &scenes = Benchmark::scenes();
@@ -50,6 +50,8 @@
             }
         }
     }
+
+    return true;
 }
 
 bool
diff --git a/src/scene-desktop.cpp b/src/scene-desktop.cpp
index 64e7550..b1431f8 100644
--- a/src/scene-desktop.cpp
+++ b/src/scene-desktop.cpp
@@ -792,10 +792,11 @@
 {
 }
 
-void
+bool
 SceneDesktop::setup()
 {
-    Scene::setup();
+    if (!Scene::setup())
+        return false;
 
     /* Parse the options */
     unsigned int windows(0);
@@ -868,6 +869,8 @@
     running_ = true;
     startTime_ = Util::get_timestamp_us() / 1000000.0;
     lastUpdateTime_ = startTime_;
+
+    return true;
 }
 
 void
diff --git a/src/scene-effect-2d.cpp b/src/scene-effect-2d.cpp
index 27275dc..9185d7b 100644
--- a/src/scene-effect-2d.cpp
+++ b/src/scene-effect-2d.cpp
@@ -295,10 +295,11 @@
     glDeleteTextures(1, &texture_);
 }
 
-void
+bool
 SceneEffect2D::setup()
 {
-    Scene::setup();
+    if (!Scene::setup())
+        return false;
 
     Texture::find_textures();
 
@@ -312,7 +313,7 @@
     if (!parse_matrix(options_["kernel"].value, kernel,
                       kernel_width, kernel_height))
     {
-        return;
+        return false;
     }
 
     /* Normalize the kernel matrix if needed */
@@ -330,12 +331,12 @@
                                                          kernel_height));
 
     if (frg_source.str().empty())
-        return;
+        return false;
 
     if (!Scene::load_shaders_from_strings(program_, vtx_source.str(),
                                           frg_source.str()))
     {
-        return;
+        return false;
     }
 
     std::vector<int> vertex_format;
@@ -358,6 +359,8 @@
     running_ = true;
     startTime_ = Util::get_timestamp_us() / 1000000.0;
     lastUpdateTime_ = startTime_;
+
+    return true;
 }
 
 void
diff --git a/src/scene-function.cpp b/src/scene-function.cpp
index 58412be..0bd9468 100644
--- a/src/scene-function.cpp
+++ b/src/scene-function.cpp
@@ -116,10 +116,11 @@
     return source.str();
 }
 
-void
+bool
 SceneFunction::setup()
 {
-    SceneGrid::setup();
+    if (!SceneGrid::setup())
+        return false;
 
     /* Parse options */
     bool vtx_function = options_["vertex-function"].value == "true";
@@ -136,7 +137,7 @@
                                                       frg_complexity));
 
     if (!Scene::load_shaders_from_strings(program_, vtx_shader, frg_shader))
-        return;
+        return false;
 
     program_.start();
 
@@ -147,6 +148,8 @@
     running_ = true;
     startTime_ = Util::get_timestamp_us() / 1000000.0;
     lastUpdateTime_ = startTime_;
+
+    return true;
 }
 
 Scene::ValidationResult
diff --git a/src/scene-grid.cpp b/src/scene-grid.cpp
index a7cc105..a9d07e0 100644
--- a/src/scene-grid.cpp
+++ b/src/scene-grid.cpp
@@ -53,10 +53,11 @@
 {
 }
 
-void
+bool
 SceneGrid::setup()
 {
-    Scene::setup();
+    if (!Scene::setup())
+        return false;
 
     int grid_size(Util::fromString<int>(options_["grid-size"].value));
     double grid_length(Util::fromString<double>(options_["grid-length"].value));
@@ -78,6 +79,8 @@
 
     currentFrame_ = 0;
     rotation_ = 0.0f;
+
+    return true;
 }
 
 void
diff --git a/src/scene-ideas.cpp b/src/scene-ideas.cpp
index 91eb34e..af716bd 100644
--- a/src/scene-ideas.cpp
+++ b/src/scene-ideas.cpp
@@ -58,6 +58,7 @@
     void update_time();
     void update_projection(const mat4& proj);
     void draw();
+    bool valid() { return valid_; }
 
 private:
     void postIdle();
@@ -223,12 +224,17 @@
 {
 }
 
-void
+bool
 SceneIdeas::setup()
 {
-    Scene::setup();
+    if (!Scene::setup())
+        return false;
+
     priv_ = new SceneIdeasPrivate();
     priv_->initialize(options_);
+    if (!priv_->valid())
+        return false;
+
     priv_->update_projection(canvas_.projection());
 
     // Core Scene state
@@ -236,6 +242,8 @@
     running_ = true;
     startTime_ = Util::get_timestamp_us() / 1000000.0;
     lastUpdateTime_ = startTime_;
+
+    return true;
 }
 
 void
diff --git a/src/scene-jellyfish.cpp b/src/scene-jellyfish.cpp
index 5253dd9..bc18ee3 100644
--- a/src/scene-jellyfish.cpp
+++ b/src/scene-jellyfish.cpp
@@ -54,20 +54,24 @@
 {
 }
 
-void
+bool
 SceneJellyfish::setup()
 {
-    Scene::setup();
+    if (!Scene::setup())
+        return false;
 
     // Set up our private object that does all of the lifting
     priv_ = new JellyfishPrivate();
-    priv_->initialize();
+    if (!priv_->initialize())
+        return false;
 
     // Set core scene timing after actual initialization so we don't measure
     // set up time.
     startTime_ = Util::get_timestamp_us() / 1000000.0;
     lastUpdateTime_ = startTime_;
     running_ = true;
+
+    return true;
 }
 
 void
@@ -107,7 +111,7 @@
 using std::string;
 using std::vector;
 
-void
+bool
 GradientRenderer::init()
 {
     // Program set up
@@ -118,7 +122,7 @@
     if (!Scene::load_shaders_from_strings(program_, vtx_source.str(),
         frg_source.str()))
     {
-        return;
+        return false;
     }
     positionLocation_ = program_["position"].location();
     uvLocation_ = program_["uvIn"].location();
@@ -144,6 +148,8 @@
     glBufferSubData(GL_ARRAY_BUFFER, uvOffset_, uvs_.size() * sizeof(vec2),
                     &uvs_.front());
     glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+    return true;
 }
 
 void
@@ -355,10 +361,24 @@
     blendFuncSrc_(0),
     blendFuncDst_(0)
 {
+}
+
+JellyfishPrivate::~JellyfishPrivate()
+{
+    positions_.clear();
+    normals_.clear();
+    colors_.clear();
+    texcoords_.clear();
+    indices_.clear();
+}
+
+bool
+JellyfishPrivate::initialize()
+{
     static const string modelFilename(GLMARK_DATA_PATH"/models/jellyfish.jobj");
     if (!load_obj(modelFilename))
     {
-        return;
+        return false;
     }
 
     // Now that we've setup the vertex data, we can setup the map of how
@@ -376,26 +396,16 @@
     dataMap_.texcoordOffset = dataMap_.colorOffset + dataMap_.colorSize;
     dataMap_.texcoordSize = texcoords_.size() * sv3;
     dataMap_.totalSize += dataMap_.texcoordSize;
-}
 
-JellyfishPrivate::~JellyfishPrivate()
-{
-    positions_.clear();
-    normals_.clear();
-    colors_.clear();
-    texcoords_.clear();
-    indices_.clear();
-}
-
-void
-JellyfishPrivate::initialize()
-{
     lastUpdateTime_ = Util::get_timestamp_us() / 1000.0;
     currentTime_ = static_cast<uint64_t>(lastUpdateTime_) % 100000000 / 1000.0;
     whichCaustic_ = static_cast<uint64_t>(currentTime_ * 30) % 32 + 1;
     rotation_ = 0.0;
 
-    gradient_.init();
+    if (!gradient_.init())
+    {
+        return false;
+    }
 
     // Set up program first so we can store attribute and uniform locations
     // away for the 
@@ -409,7 +419,7 @@
     if (!Scene::load_shaders_from_strings(program_, vtx_source.str(),
         frg_source.str()))
     {
-        return;
+        return false;
     }
 
     // Stash away attribute and uniform locations for handy use.
@@ -452,6 +462,7 @@
     if (!gotTex || textureObjects_[0] == 0)
     {
         Log::error("Jellyfish texture set up failed!!!\n");
+        return false;
     }
     glBindTexture(GL_TEXTURE_2D, 0);
     // Then, the caustics textures
@@ -467,6 +478,7 @@
         if (!gotTex || textureObjects_[i] == 0)
         {
             Log::error("Caustics texture[%u] set up failed!!!\n", i);
+            return false;
         }
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
@@ -484,6 +496,8 @@
     glEnable(GL_BLEND);
     glDisable(GL_CULL_FACE);
     glDisable(GL_DEPTH_TEST);
+
+    return true;
 }
 
 void
diff --git a/src/scene-jellyfish.h b/src/scene-jellyfish.h
index 77be159..1e0db3a 100644
--- a/src/scene-jellyfish.h
+++ b/src/scene-jellyfish.h
@@ -48,7 +48,7 @@
         vertices_.clear();
         uvs_.clear();
     }
-    void init();
+    bool init();
     void cleanup();
     void draw();
 };
@@ -114,7 +114,7 @@
 public:
     JellyfishPrivate();
     ~JellyfishPrivate();
-    void initialize();
+    bool initialize();
     void update_viewport(const LibMatrix::vec2& viewport);
     void update_time();
     void cleanup();
diff --git a/src/scene-loop.cpp b/src/scene-loop.cpp
index 5e3f095..564a2fe 100644
--- a/src/scene-loop.cpp
+++ b/src/scene-loop.cpp
@@ -110,10 +110,11 @@
 }
 
 
-void
+bool
 SceneLoop::setup()
 {
-    SceneGrid::setup();
+    if (!SceneGrid::setup())
+        return false;
 
     /* Parse options */
     bool vtx_loop = options_["vertex-loop"].value == "true";
@@ -130,7 +131,7 @@
                                                       frg_uniform));
 
     if (!Scene::load_shaders_from_strings(program_, vtx_shader, frg_shader))
-        return;
+        return false;
 
     program_.start();
 
@@ -144,6 +145,8 @@
     running_ = true;
     startTime_ = Util::get_timestamp_us() / 1000000.0;
     lastUpdateTime_ = startTime_;
+
+    return true;
 }
 
 Scene::ValidationResult
diff --git a/src/scene-pulsar.cpp b/src/scene-pulsar.cpp
index 7d10dbd..6ab661c 100644
--- a/src/scene-pulsar.cpp
+++ b/src/scene-pulsar.cpp
@@ -73,10 +73,11 @@
 {
 }
 
-void
+bool
 ScenePulsar::setup()
 {
-    Scene::setup();
+    if (!Scene::setup())
+        return false;
 
     // Disable back-face culling
     glDisable(GL_CULL_FACE);
@@ -119,8 +120,8 @@
     if (options_["texture"].value == "true") {
         frg_shader_filename = GLMARK_DATA_PATH"/shaders/light-basic-tex.frag";
         Texture::find_textures();
-        Texture::load("crate-base", &texture_,
-                      GL_NEAREST, GL_NEAREST, 0);
+        if (!Texture::load("crate-base", &texture_, GL_NEAREST, GL_NEAREST, 0))
+            return false;
 
     } else {
         frg_shader_filename = GLMARK_DATA_PATH"/shaders/light-basic.frag";
@@ -136,7 +137,7 @@
     if (!Scene::load_shaders_from_strings(program_, vtx_source.str(),
                                           frg_source.str()))
     {
-        return;
+        return false;
     }
 
     create_and_setup_mesh();
@@ -148,6 +149,8 @@
     running_ = true;
     startTime_ = Util::get_timestamp_us() / 1000000.0;
     lastUpdateTime_ = startTime_;
+
+    return true;
 }
 
 void
diff --git a/src/scene-shading.cpp b/src/scene-shading.cpp
index e760209..22b5ad5 100644
--- a/src/scene-shading.cpp
+++ b/src/scene-shading.cpp
@@ -132,10 +132,11 @@
     return source.str();
 }
 
-void
+bool
 SceneShading::setup()
 {
-    Scene::setup();
+    if (!Scene::setup())
+        return false;
 
     static const LibMatrix::vec4 lightPosition(20.0f, 20.0f, 10.0f, 1.0f);
     static const LibMatrix::vec4 materialDiffuse(0.0f, 0.0f, 1.0f, 1.0f);
@@ -181,7 +182,7 @@
     if (!Scene::load_shaders_from_strings(program_, vtx_source.str(),
                                           frg_source.str()))
     {
-        return;
+        return false;
     }
 
     Model model;
@@ -189,7 +190,7 @@
     bool modelLoaded = model.load(whichModel);
 
     if(!modelLoaded)
-        return;
+        return false;
 
     // Now that we're successfully loaded, there are a few quirks about
     // some of the known models that we need to account for.  The draw
@@ -257,6 +258,8 @@
     running_ = true;
     startTime_ = Util::get_timestamp_us() / 1000000.0;
     lastUpdateTime_ = startTime_;
+
+    return true;
 }
 
 void
diff --git a/src/scene-terrain.cpp b/src/scene-terrain.cpp
index 9b5bd22..bf8e18b 100644
--- a/src/scene-terrain.cpp
+++ b/src/scene-terrain.cpp
@@ -231,6 +231,21 @@
 }
 
 bool
+SceneTerrain::supported(bool show_errors)
+{
+    GLint vertex_textures;
+    glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &vertex_textures);
+
+    if (show_errors && vertex_textures <= 0) {
+        Log::error("SceneTerrain requires Vertex Texture Fetch support, "
+                   "but GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS is %d\n",
+                   vertex_textures);
+    }
+    
+    return vertex_textures > 0;
+}
+
+bool
 SceneTerrain::load()
 {
     Scene::load();
@@ -246,23 +261,11 @@
     Scene::unload();
 }
 
-void
+bool
 SceneTerrain::setup()
 {
-    Scene::setup();
-
-    /* Ensure implementation supports vertex texture fetch */
-    GLint vertex_textures;
-    glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &vertex_textures);
-    if (vertex_textures <= 0) {
-        Log::error("SceneTerrain requires Vertex Texture Fetch support, "
-                   "but GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS is %d\n",
-                   vertex_textures);
-        currentFrame_ = 0;
-        startTime_ = Util::get_timestamp_us() / 1000000.0;
-        running_ = false;
-        return;
-    }
+    if (!Scene::setup())
+        return false;
 
     /* Parse options */
     float repeat = Util::fromString<double>(options_["repeat-overlay"].value);
@@ -313,6 +316,8 @@
     currentFrame_ = 0;
     startTime_ = Util::get_timestamp_us() / 1000000.0;
     running_ = true;
+
+    return true;
 }
 
 void
diff --git a/src/scene-texture.cpp b/src/scene-texture.cpp
index 62bd6eb..12aec1b 100644
--- a/src/scene-texture.cpp
+++ b/src/scene-texture.cpp
@@ -103,10 +103,11 @@
     mesh_.reset();
 }
 
-void
+bool
 SceneTexture::setup()
 {
-    Scene::setup();
+    if (!Scene::setup())
+        return false;
 
     static const std::string vtx_shader_filename(GLMARK_DATA_PATH"/shaders/light-basic.vert");
     static const std::string vtx_shader_texgen_filename(GLMARK_DATA_PATH"/shaders/light-basic-texgen.vert");
@@ -138,8 +139,8 @@
     }
 
     const string& whichTexture(options_["texture"].value);
-    Texture::load(whichTexture, &texture_,
-                  min_filter, mag_filter, 0);
+    if (!Texture::load(whichTexture, &texture_, min_filter, mag_filter, 0))
+        return false;
 
     // Load shaders
     bool doTexGen(options_["texgen"].value == "true");
@@ -168,14 +169,14 @@
     if (!Scene::load_shaders_from_strings(program_, vtx_source.str(),
                                           frg_source.str()))
     {
-        return;
+        return false;
     }
 
     Model model;
     const string& whichModel(options_["model"].value);
     bool modelLoaded = model.load(whichModel);
     if(!modelLoaded)
-        return;
+        return false;
 
     // Now that we're successfully loaded, there are a few quirks about
     // some of the known models that we need to account for.  The draw
@@ -251,6 +252,8 @@
     running_ = true;
     startTime_ = Util::get_timestamp_us() / 1000000.0;
     lastUpdateTime_ = startTime_;
+
+    return true;
 }
 
 void
diff --git a/src/scene.cpp b/src/scene.cpp
index be6dd9a..975537d 100644
--- a/src/scene.cpp
+++ b/src/scene.cpp
@@ -76,6 +76,13 @@
 }
 
 bool
+Scene::supported(bool show_errors)
+{
+    static_cast<void>(show_errors);
+    return true;
+}
+
+bool
 Scene::load()
 {
     return true;
@@ -86,7 +93,7 @@
 {
 }
 
-void
+bool
 Scene::setup()
 {
     duration_ = Util::fromString<double>(options_["duration"].value);
@@ -101,6 +108,12 @@
             ShaderSource::ShaderTypeFragment
             );
 
+    currentFrame_ = 0;
+    running_ = false;
+    startTime_ = Util::get_timestamp_us() / 1000000.0;
+    lastUpdateTime_ = startTime_;
+
+    return supported(true);
 }
 
 void
diff --git a/src/scene.h b/src/scene.h
index c52239a..a455fbd 100644
--- a/src/scene.h
+++ b/src/scene.h
@@ -74,6 +74,15 @@
     };
 
     /**
+     * Checks whether this scene (in its current configuration) is supported.
+     *
+     * @param show_errors whether to log errors about unsupported features
+     *
+     * @return whether the scene is supported
+     */
+    virtual bool supported(bool show_errors);
+
+    /**
      * Performs option-independent resource loading and configuration.
      *
      * It should be safe to call ::load() (and the corresponding ::unload())
@@ -95,9 +104,12 @@
      * This method also prepares a scene for a benchmark run.
      * It should be called just before running a scene/benchmark.
      *
-     * @return the operation status
+     * The base Scene::setup() method also checks whether a scene
+     * configuration is supported by calling ::supported(true).
+     *
+     * @return whether setting the scene up succeeded
      */
-    virtual void setup();
+    virtual bool setup();
 
     /**
      * Performs option-dependent resource unloading.
@@ -232,7 +244,7 @@
 public:
     SceneDefaultOptions(Canvas &pCanvas) : Scene(pCanvas, "") {}
     bool set_option(const std::string &opt, const std::string &val);
-    void setup();
+    bool setup();
 
 private:
     std::list<std::pair<std::string, std::string> > defaultOptions_;
@@ -244,7 +256,7 @@
     SceneBuild(Canvas &pCanvas);
     bool load();
     void unload();
-    void setup();
+    bool setup();
     void teardown();
     void update();
     void draw();
@@ -272,7 +284,7 @@
     SceneTexture(Canvas &pCanvas);
     bool load();
     void unload();
-    void setup();
+    bool setup();
     void teardown();
     void update();
     void draw();
@@ -300,7 +312,7 @@
     SceneShading(Canvas &pCanvas);
     bool load();
     void unload();
-    void setup();
+    bool setup();
     void teardown();
     void update();
     void draw();
@@ -327,7 +339,7 @@
     SceneGrid(Canvas &pCanvas, const std::string &name);
     virtual bool load();
     virtual void unload();
-    virtual void setup();
+    virtual bool setup();
     virtual void teardown();
     virtual void update();
     virtual void draw();
@@ -346,7 +358,7 @@
 {
 public:
     SceneConditionals(Canvas &pCanvas);
-    void setup();
+    bool setup();
     ValidationResult validate();
 
     ~SceneConditionals();
@@ -356,7 +368,7 @@
 {
 public:
     SceneFunction(Canvas &pCanvas);
-    void setup();
+    bool setup();
     ValidationResult validate();
 
     ~SceneFunction();
@@ -366,7 +378,7 @@
 {
 public:
     SceneLoop(Canvas &pCanvas);
-    void setup();
+    bool setup();
     ValidationResult validate();
 
     ~SceneLoop();
@@ -378,7 +390,7 @@
     SceneBump(Canvas &pCanvas);
     bool load();
     void unload();
-    void setup();
+    bool setup();
     void teardown();
     void update();
     void draw();
@@ -393,10 +405,10 @@
     float rotation_;
     float rotationSpeed_;
 private:
-    void setup_model_plain(const std::string &type);
-    void setup_model_normals();
-    void setup_model_normals_tangent();
-    void setup_model_height();
+    bool setup_model_plain(const std::string &type);
+    bool setup_model_normals();
+    bool setup_model_normals_tangent();
+    bool setup_model_height();
 };
 
 class SceneEffect2D : public Scene
@@ -405,7 +417,7 @@
     SceneEffect2D(Canvas &pCanvas);
     bool load();
     void unload();
-    void setup();
+    bool setup();
     void teardown();
     void update();
     void draw();
@@ -426,7 +438,7 @@
     ScenePulsar(Canvas &pCanvas);
     bool load();
     void unload();
-    void setup();
+    bool setup();
     void teardown();
     void update();
     void draw();
@@ -455,7 +467,7 @@
     SceneDesktop(Canvas &canvas);
     bool load();
     void unload();
-    void setup();
+    bool setup();
     void teardown();
     void update();
     void draw();
@@ -473,9 +485,10 @@
 {
 public:
     SceneBuffer(Canvas &canvas);
+    bool supported(bool show_errors);
     bool load();
     void unload();
-    void setup();
+    bool setup();
     void teardown();
     void update();
     void draw();
@@ -495,7 +508,7 @@
     SceneIdeas(Canvas &pCanvas);
     bool load();
     void unload();
-    void setup();
+    bool setup();
     void teardown();
     void update();
     void draw();
@@ -513,9 +526,10 @@
 {
 public:
     SceneTerrain(Canvas &pCanvas);
+    bool supported(bool show_errors);
     bool load();
     void unload();
-    void setup();
+    bool setup();
     void teardown();
     void update();
     void draw();
@@ -536,7 +550,7 @@
     ~SceneJellyfish();
     bool load();
     void unload();
-    void setup();
+    bool setup();
     void teardown();
     void update();
     void draw();