Skip to content

Instantly share code, notes, and snippets.

View expenses's full-sized avatar
❤️‍🔥

Ashley expenses

❤️‍🔥
View GitHub Profile
@expenses
expenses / nodes.md
Created February 2, 2024 01:12
Networking Model

I'm building a decentralized system for syncing a 3D scene between a set of nodes in real-time. Currently I'm focused on small fully-connected networks where each connection between nodes has been explicitly approved by each party (think of situations like a group of 5 friends connecting to each other).

Each node maintains a set of approved node IDs that it is allowed to connect to. When explicitly instructed to dial a remote, the remote node ID is simply added to the set. When accepting a connection the approved node set is checked and if it doesn't contain the remote then a message is sent along a channel to the UI and is then awaited on for a response. If denied then the connection just closes. In the future I plan on having a set of blocked nodes to which connections are automatically denied.

For reference, here's what this interface currently looks like:

20240202_13h42m17s_grim

There should be only one connection per re

@expenses
expenses / prefix_sum.glsl
Created December 7, 2023 05:12
prefix_sum.glsl
//struct PrefixSumValue {
// uint32_t index;
// uint32_t sum;
//};
layout(buffer_reference, scalar) buffer PrefixSumBuffer {
uint64_t counter;
PrefixSumValue values[];
};
@expenses
expenses / GPU.md
Last active February 16, 2024 02:46
Ashley's opinionated notes on writing a modern renderer

Edit: Moved to https://github.com/expenses/renderer-opinions.

GPU-driven rendering

The most important term you need to know is 'gpu-driven rendering'.

With a regular 'cpu-driven' pipeline the cpu has the authority on whats being rendered. It culls objects and issues commands like bind this texture, bind that texture, draw this, draw that etc. In this situation the GPU only recieves information about what the scene is like in bits and pieces, and can spend a significant amount of time switching between different textures and buffers, both of which add overhead. In technical terms, both the CPU and GPU are doing O(n) work with respect to the amount of geometry and textures.

In a gpu-driven pipeline, the GPU has all the scene information available to it simultaneously, and is able to manage it's own work via writing to an on-gpu buffer of draw calls to make. Objects can be culled beforehand via compute shaders without any information needing to be read back to the CPU. The CPU does O(1) work, basically jus

from pxr import Usd, UsdGeom, UsdShade, Sdf, Gf, Tf
import bpy
from bpy.props import StringProperty
from bpy_extras.io_utils import ImportHelper
from bpy.types import Operator
import copy
import os
import itertools
stage = None
pub const DFD_TABLE: [&'static [u8]; 185] = [
&[],
&[
60, 0, 0, 0, 0, 0, 0, 0, 2, 0, 56, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0,
0, 0, 0, 0, 15, 0, 0, 0, 4, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0,
],
&[
92, 0, 0, 0, 0, 0, 0, 0, 2, 0, 88, 0, 1, 1, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 15, 0, 0, 0, 0,
0, 0, 0, 0, 15, 0, 0, 0, 4, 0, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 8, 0, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0,
15, 0, 0, 0, 12, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0,
@expenses
expenses / blender_xatlas.py
Last active October 4, 2023 09:49
blender_xatlas.py
# coding: utf-8
import bpy
import xatlas
import os
import bmesh
import sys
atlas = xatlas.Atlas()
bmeshes = []
fn main() {
let filename = std::env::args().nth(1).unwrap();
let image = image::open(&filename).unwrap();
let mut max: f32 = -10000.0;
let mut min: f32 = 10000.0;
if let image::DynamicImage::ImageRgb32F(image) = image {
for pixel in image.pixels() {
./build/cli -inputPath $1 -targetFormat R32G32B32A32_SFLOAT -outCubeMap $1_cubemap.ktx2 -distribution Ggx -cubeMapResolution 1024 &&
ktx2-to-sphere-harmonics $1_cubemap.ktx2 $1_sphere_harmonics.bin &&
ktx2-bc6h-compress $1_cubemap.ktx2 $1_cubemap.compressed.ktx2 --sphere-harmonics-file $1_sphere_harmonics.bin
ktx2-to-dds $1_cubemap.compressed.ktx2 $1_cubemap.compressed.dds &&
ktx2-info $1_cubemap.compressed.ktx2 &&
fn write_bytes_to_texture(
queue: &wgpu::Queue,
texture: &wgpu::Texture,
mip: u32,
bytes: &[u8],
desc: &wgpu::TextureDescriptor,
) {
let format_info = desc.format.describe();

Goal

We want to build a renderer that is capable of drawing 1000 unique avatars (with unique models and textures) in a single draw call. This is doable today with wgpu on native with multi_draw_indirect (See https://vkguide.dev/docs/gpudriven/gpu_driven_engines/ for a great overview!).

This has some requirements. As we cannot switch vertex/index buffers in the multi_draw_indirect calls, we need all geometry that we're drawing to be in a few large buffers that are bound beforehand. Additionally, any textures that we're using all need to be accessible from within the fragment shader. These requirements mean that adapting an existing renderer such as BabylonJS, Unity or bevy's renderer would all take a l-o-n-g time (not that this makes any of these renderers bad) (the bevy guys at least know about this). It'd be much, much easier to start something from scratch.

For a