Inside the companion
First Light
Make your first CPU image, rasterize triangles, and trace an allocation budget.
Project files 15
Check the image contracts
Independent scene, geometry, pixel, rasterization, and memory boundary checks.
#include "prism/renderer.hpp"
#include "prism/memory_lab.hpp"
#include "prism/raster_lab.hpp"
#include <array>
#include <cmath>
#include <fstream>
#include <iostream>
#include <iterator>
#include <limits>
#include <locale>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
namespace {
std::size_t checks{};
void require(bool condition, const char* message) {
++checks;
if (!condition) throw std::runtime_error{message};
}
prism::Scene minimal() {
return {1, "Test scene", 1, 1, {{0, 0, 3}, {0, 0, 0}, 60},
{0, 1, 0}, {0, 0, 1}, -1, {1, 1, 1}, {}};
}
void domain_contract() {
auto scene = minimal();
require(prism::validate_scene(scene).has_value(), "minimal scene is valid");
scene.version = 2;
require(!prism::validate_scene(scene), "unsupported typed scene contract is rejected");
scene = minimal();
scene.width = 0;
require(!prism::render(scene), "zero width is rejected at render boundary");
scene = minimal(); scene.width = prism::max_dimension + 1;
require(!prism::validate_scene(scene), "dimension limit is enforced");
scene = minimal(); scene.width = 2048; scene.height = 1024;
require(prism::validate_scene(scene).has_value(), "exact pixel budget is valid");
scene.height = 1025;
require(!prism::validate_scene(scene), "pixel product beyond budget is rejected");
scene = minimal(); scene.camera.look_at = scene.camera.position;
require(!prism::validate_scene(scene), "degenerate camera is rejected");
scene = minimal(); scene.camera.look_at = {0, 1, 3};
require(!prism::validate_scene(scene), "world-up parallel camera is rejected");
scene = minimal(); scene.light_direction = {0, 0, 0};
require(!prism::validate_scene(scene), "zero light cannot be normalized");
scene = minimal(); scene.background.r = std::numeric_limits<double>::quiet_NaN();
require(!prism::validate_scene(scene), "NaN is rejected for caller-built scenes");
scene = minimal(); scene.ground_y = std::numeric_limits<double>::infinity();
require(!prism::validate_scene(scene), "infinity is rejected for caller-built scenes");
scene = minimal(); scene.spheres.push_back({{0, 0, 0}, 0, {1, 0, 0}});
require(!prism::validate_scene(scene), "zero radius is rejected");
scene = minimal(); scene.spheres.resize(prism::max_spheres + 1, {{0, 0, 0}, 1, {1, 0, 0}});
require(!prism::validate_scene(scene), "sphere count is bounded");
scene.spheres.resize(prism::max_spheres); scene.width = 2048; scene.height = 1024;
require(!prism::validate_scene(scene), "combined scene/render work is bounded");
scene = minimal(); scene.title = "bad\nname";
require(!prism::validate_scene(scene), "diagnostic title controls are rejected");
scene = minimal(); scene.camera.vertical_fov_degrees = 121;
require(!prism::validate_scene(scene), "field of view is bounded");
scene = minimal(); scene.spheres.push_back({{1001, 0, 0}, 1, {1, 0, 0}});
require(!prism::validate_scene(scene), "sphere coordinates are bounded before math");
auto owner = prism::demo_scene();
const auto copy = owner;
owner.title.assign("changed"); owner.spheres.clear();
require(copy.title != owner.title && copy.spheres.size() == 3, "typed scenes own independent copied storage");
}
void rendering_contract() {
auto scene = minimal();
const auto background = prism::render(scene);
require(background && background->rgb == std::vector<std::uint8_t>{0, 255, 0}, "central empty ray returns exact green background");
scene.spheres.push_back({{0, 0, 0}, 1, {1, 0, 0}});
const auto sphere = prism::render(scene);
require(sphere && sphere->rgb == std::vector<std::uint8_t>{255, 0, 0}, "sphere ray intersection and front light give exact red");
scene.spheres.push_back({{0, 0, -3}, 1, {0, 0, 1}});
const auto near_first = prism::render(scene);
std::swap(scene.spheres[0], scene.spheres[1]);
const auto far_first = prism::render(scene);
require(near_first && far_first && near_first->rgb == sphere->rgb && far_first->rgb == sphere->rgb,
"nearest positive sphere hit wins in both submission orders");
scene.spheres = {{{1, 0, 0}, 1, {1, 0, 0}}};
scene.light_direction = {-1, 0, 0};
const auto tangent = prism::render(scene);
require(tangent && tangent->rgb == std::vector<std::uint8_t>{255, 0, 0},
"axis-aligned tangent ray has one accepted lit hit");
scene.spheres = {{{0, 0, 0}, 1, {1, 0, 0}}};
scene.light_direction = {1, 0, 1};
const auto lit = prism::render(scene);
scene.spheres.push_back({{1, 0, 2}, 0.25, {1, 1, 1}});
const auto shadowed = prism::render(scene);
require(lit && shadowed && lit->rgb[0] > 108 && shadowed->rgb == std::vector<std::uint8_t>{108, 0, 0},
"sphere outside camera ray blocks directional light and leaves ambient red");
scene.spheres.pop_back(); scene.light_direction = {0, 0, 1};
scene.camera.position = {0, 0, 0}; scene.camera.look_at = {0, 0, -1};
const auto inside = prism::render(scene);
require(inside && inside->rgb == std::vector<std::uint8_t>{108, 0, 0}, "inside sphere exit root shades ambient red rather than green background");
scene = minimal(); scene.camera.position = {0, 0, 1000}; scene.camera.look_at = {0, 0, -1000};
scene.spheres = {{{0, 0, -999}, 0.001, {1, 0, 0}}};
const auto small_distant = prism::render(scene);
require(small_distant && small_distant->rgb == std::vector<std::uint8_t>{255, 0, 0},
"supported minimum-radius sphere has a finite lit central hit at large distance");
scene = minimal(); scene.width = 32; scene.height = 24;
const auto first = prism::render(scene), second = prism::render(scene);
require(first && second && first->rgb == second->rgb, "render is deterministic for repeated input");
require(first->pixels().size() == 32 * 24 * 3, "image owns exactly width*height*3 RGB bytes");
const auto demo = prism::demo_scene();
require(prism::validate_scene(demo).has_value(), "typed studio scene is valid");
const auto rendered = prism::render(demo);
require(rendered && rendered->rgb.size() == 960 * 600 * 3, "studio scene renders its complete image");
}
void ppm_contract() {
// No shared fixed temporary filename: isolate the test output in a unique
// directory and clean it through an RAII guard even if an assertion throws.
const auto base = std::filesystem::temp_directory_path();
std::filesystem::path folder;
for (unsigned i = 0; i < 1000; ++i) {
auto candidate = base / ("prism-native-test-" + std::to_string(i));
if (std::filesystem::create_directory(candidate)) { folder = std::move(candidate); break; }
}
require(!folder.empty(), "temporary test directory created");
struct Cleanup { std::filesystem::path path; ~Cleanup() { std::error_code e; std::filesystem::remove_all(path, e); } } cleanup{folder};
const auto image = prism::render(minimal());
const auto path = folder / "pixel.ppm";
require(prism::write_ppm(path, *image).has_value(), "PPM writes successfully");
std::ifstream file{path, std::ios::binary};
const std::string contents{std::istreambuf_iterator<char>{file}, {}};
const std::string expected = std::string{"P6\n1 1\n255\n"} + std::string{"\0\xff\0", 3};
require(contents == expected, "PPM header and payload are exact");
auto invalid_image = *image; invalid_image.rgb.clear();
require(!prism::write_ppm(folder / "bad.ppm", invalid_image), "image shape mismatch is rejected before opening output");
require(!std::filesystem::exists(folder / "bad.ppm"), "invalid image creates no output");
require(!prism::write_ppm(folder / "missing" / "out.ppm", *image), "unwritable destination returns I/O error");
struct GroupedNumbers final : std::numpunct<char> {
char do_thousands_sep() const override { return ','; }
std::string do_grouping() const override { return "\3"; }
};
struct RestoreLocale {
std::locale previous;
~RestoreLocale() { std::locale::global(previous); }
} restore{std::locale{}};
// Independent of any installed locale: exercise a valid embedding app's
// grouping convention without allowing it into the PPM numeric grammar.
std::locale::global(std::locale{std::locale::classic(), new GroupedNumbers});
const prism::Image wide{1000, 1, std::vector<std::uint8_t>(3000)};
const auto localized_path = folder / "localized.ppm";
require(prism::write_ppm(localized_path, wide).has_value(), "PPM writes under a grouped global locale");
std::ifstream localized_file{localized_path, std::ios::binary};
const std::string localized_contents{std::istreambuf_iterator<char>{localized_file}, {}};
require(localized_contents.starts_with("P6\n1000 1\n255\n") && localized_contents.size() == 3014,
"PPM header remains ungrouped ASCII under a grouped global locale");
}
void allocator_contract() {
const auto report = prism::run_memory_lab();
require(report.bounded_failure, "bounded arena exhausts and preserves existing vector values");
require(report.live_after_scope == 0, "all borrowed containers die before resource release");
require(report.allocations >= 1 && report.peak_bytes >= 16 * sizeof(int) && report.peak_bytes <= 512,
"allocation accounting covers the vector without assuming a string's small-buffer capacity");
require(report.promoted_title == "A title promoted beyond the arena lifetime", "promoted owner survives arena reset and destruction");
prism::CountingResource failing{*std::pmr::null_memory_resource()};
bool failed = false;
try { std::pmr::vector<int> values{&failing}; values.reserve(1); }
catch (const std::bad_alloc&) { failed = true; }
require(failed && failing.allocations() == 0 && failing.live_bytes() == 0, "first-allocation failure leaves counters unchanged");
}
prism::Triangle flat_triangle(double depth, prism::Color color) {
return {{{{0, 0, depth, color}, {2, 0, depth, color}, {0, 2, depth, color}}}};
}
std::array<std::uint8_t, 3> pixel(const prism::Image& image, std::size_t x, std::size_t y) {
const auto offset = (y * image.width + x) * 3;
return {image.rgb.at(offset), image.rgb.at(offset + 1), image.rgb.at(offset + 2)};
}
void raster_contract() {
prism::RasterScene scene{2, 2, {0, 0, 0}, {flat_triangle(0.5, {1, 0, 0})}};
auto image = prism::rasterize(scene);
require(image && image->rgb.size() == 12, "raster image has exact RGB storage");
require(pixel(*image, 0, 0) == std::array<std::uint8_t, 3>{255, 0, 0}, "edge functions cover an interior pixel");
require(pixel(*image, 1, 1) == std::array<std::uint8_t, 3>{0, 0, 0}, "edge functions reject an exterior pixel");
std::swap(scene.triangles[0].vertices[1], scene.triangles[0].vertices[2]);
auto reversed = prism::rasterize(scene);
require(reversed && reversed->rgb == image->rgb, "both triangle windings produce identical coverage");
scene.triangles[0] = {{{{0, 0, 0.5, {1, 0, 0}}, {2, 0, 0.5, {0, 1, 0}}, {0, 2, 0.5, {0, 0, 1}}}}};
image = prism::rasterize(scene);
require(image && pixel(*image, 0, 0) == std::array<std::uint8_t, 3>{180, 128, 128}, "barycentric colors interpolate in linear RGB before gamma encoding");
scene.triangles = {flat_triangle(0.8, {1, 0, 0}), flat_triangle(0.2, {0, 0, 1})};
const auto far_first = prism::rasterize(scene);
std::swap(scene.triangles[0], scene.triangles[1]);
const auto near_first = prism::rasterize(scene);
require(far_first && near_first && far_first->rgb == near_first->rgb, "depth buffer chooses nearest independent of submission order");
require(pixel(*near_first, 0, 0) == std::array<std::uint8_t, 3>{0, 0, 255}, "near triangle owns the overlap pixel");
scene.triangles = {flat_triangle(0.2, {1, 0, 0}), flat_triangle(0.2, {0, 0, 1})};
image = prism::rasterize(scene);
require(image && pixel(*image, 0, 0) == std::array<std::uint8_t, 3>{255, 0, 0}, "exact depth ties preserve first submitted sample");
const prism::Triangle lower{{{{2, 0, 0.5, {0, 0, 1}}, {2, 2, 0.5, {0, 0, 1}}, {0, 2, 0.5, {0, 0, 1}}}}};
scene.triangles = {flat_triangle(0.5, {1, 0, 0}), lower};
const auto shared_first = prism::rasterize(scene);
std::swap(scene.triangles[0], scene.triangles[1]);
const auto shared_second = prism::rasterize(scene);
require(shared_first && shared_second &&
pixel(*shared_first, 1, 0) == std::array<std::uint8_t, 3>{255, 0, 0} &&
pixel(*shared_second, 1, 0) == std::array<std::uint8_t, 3>{0, 0, 255},
"inclusive shared edge is covered by both triangles and first exact tie wins");
scene.triangles = {flat_triangle(1.0, {1, 0, 0})};
const auto far_boundary = prism::rasterize(scene);
require(far_boundary && pixel(*far_boundary, 0, 0) == std::array<std::uint8_t, 3>{255, 0, 0},
"far-depth endpoint remains drawable against the infinite clear value");
scene.width = 4; scene.height = 4;
const prism::Triangle varying{{{{0, 0, 0, {1, 0, 0}}, {4, 0, 1, {1, 0, 0}}, {0, 4, 1, {1, 0, 0}}}}};
const prism::Triangle constant{{{{0, 0, 0.5, {0, 1, 0}}, {4, 0, 0.5, {0, 1, 0}}, {0, 4, 0.5, {0, 1, 0}}}}};
scene.triangles = {varying, constant};
image = prism::rasterize(scene);
require(image && pixel(*image, 0, 0) == std::array<std::uint8_t, 3>{255, 0, 0} &&
pixel(*image, 2, 0) == std::array<std::uint8_t, 3>{0, 255, 0}, "interpolated per-pixel depth changes which triangle wins");
scene = {2, 2, {0, 0, 0}, {{{{{0, 0, 0.5, {1, 0, 0}}, {1, 1, 0.5, {1, 0, 0}}, {2, 2, 0.5, {1, 0, 0}}}}}}};
image = prism::rasterize(scene);
require(image && image->rgb == std::vector<std::uint8_t>(12, 0), "degenerate triangle covers nothing and never divides by zero");
scene.triangles[0] = {{{{-2, -2, 0.5, {1, 0, 0}}, {4, -2, 0.5, {1, 0, 0}}, {-2, 4, 0.5, {1, 0, 0}}}}};
image = prism::rasterize(scene);
require(image && pixel(*image, 0, 0)[0] == 255 && image->rgb.size() == 12, "partly off-screen bounding box is clamped before indexing");
scene.triangles[0] = {{{{-20, -20, 0.5, {1, 0, 0}}, {-18, -20, 0.5, {1, 0, 0}}, {-20, -18, 0.5, {1, 0, 0}}}}};
image = prism::rasterize(scene);
require(image && image->rgb == std::vector<std::uint8_t>(12, 0), "fully off-screen triangle performs no pixel writes");
scene.triangles = {flat_triangle(0.5, {1, 0, 0})};
scene.triangles[0].vertices[0].x = std::numeric_limits<double>::infinity();
require(!prism::rasterize(scene), "non-finite screen coordinate is rejected before conversion");
scene.triangles[0] = flat_triangle(0.5, {1, 0, 0});
scene.triangles[0].vertices[0].x = prism::max_screen_coordinate + 1;
require(!prism::validate_raster_scene(scene), "screen-coordinate bound is enforced");
scene.triangles[0] = flat_triangle(-0.01, {1, 0, 0});
require(!prism::validate_raster_scene(scene), "out-of-domain depth is rejected");
scene.triangles[0] = flat_triangle(std::numeric_limits<double>::quiet_NaN(), {1, 0, 0});
require(!prism::validate_raster_scene(scene), "NaN depth is rejected");
scene.triangles.assign(prism::max_triangles + 1, flat_triangle(0.5, {1, 0, 0}));
require(!prism::validate_raster_scene(scene), "triangle count is bounded");
scene.triangles.resize(8); scene.width = 2048; scene.height = 1024;
require(prism::validate_raster_scene(scene).has_value(), "exact raster-work budget is valid");
scene.triangles.push_back(flat_triangle(0.5, {1, 0, 0}));
require(!prism::validate_raster_scene(scene), "combined raster-work budget is enforced");
const auto demo = prism::rasterize(prism::demo_raster_scene());
require(demo && demo->rgb.size() == 960 * 600 * 3, "typed raster demo produces its full image");
}
} // namespace
int main() {
try {
domain_contract(); rendering_contract(); ppm_contract(); allocator_contract(); raster_contract();
std::cout << checks << " native contract checks passed.\n";
return 0;
} catch (const std::exception& error) {
std::cerr << "Native test failed: " << error.what() << '\n';
return 1;
}
}
This is the file included with the course. Copy and download give you its complete source.
Taking your work offline? Download all course companions
