mirror of
https://github.com/stevenrobertson/cuburn.git
synced 2025-03-13 06:51:28 -04:00
Make code more portable
This commit is contained in:
parent
eeff0a4d4f
commit
c605815130
170
cuburn/_pyflam3_hacks.py
Normal file
170
cuburn/_pyflam3_hacks.py
Normal file
@ -0,0 +1,170 @@
|
||||
"""
|
||||
Fr0st's pyflam3 bindings omit the ctypes fields for the xform structure.
|
||||
This lets a given fr0st release be compatible with a much larger range
|
||||
of flam3 versions, but we need access to that data.
|
||||
|
||||
Importing this module monkey-patches the fr0st module to include those
|
||||
fields, and also provides a few other functions not needed in fr0st.
|
||||
If flam3 or smoulder eventually includes padded xform structuress for
|
||||
wider compatibility, or I bother to write a parser for flam3.h,
|
||||
this may become unnecessary.
|
||||
"""
|
||||
|
||||
from ctypes import *
|
||||
from fr0stlib.pyflam3 import constants
|
||||
from fr0stlib.pyflam3._flam3 import *
|
||||
|
||||
flam3_nvariations = constants.flam3_nvariations = 99
|
||||
|
||||
BaseXForm._fields_ = [('var', c_double * flam3_nvariations)
|
||||
, ('c', (c_double * 2) * 3) # TODO: ctypes 2D arrays?
|
||||
, ('post', (c_double * 2) * 3)
|
||||
, ('density', c_double)
|
||||
, ('color', c_double)
|
||||
, ('color_speed', c_double)
|
||||
, ('animate', c_double)
|
||||
, ('opacity', c_double)
|
||||
, ('vis_adjusted', c_double)
|
||||
, ('padding', c_int)
|
||||
, ('wind', c_double * 2)
|
||||
, ('precalc_angles_flag', c_int)
|
||||
, ('precalc_atan_xy_flag', c_int)
|
||||
, ('precalc_atan_yx_flag', c_int)
|
||||
, ('has_preblur', c_double)
|
||||
, ('has_post', c_int)
|
||||
, ('blob_low', c_double)
|
||||
, ('blob_high', c_double)
|
||||
, ('blob_waves', c_double)
|
||||
, ('pdj_a', c_double)
|
||||
, ('pdj_b', c_double)
|
||||
, ('pdj_c', c_double)
|
||||
, ('pdj_d', c_double)
|
||||
, ('fan2_x', c_double)
|
||||
, ('fan2_y', c_double)
|
||||
, ('rings2_val', c_double)
|
||||
, ('perspective_angle', c_double)
|
||||
, ('perspective_dist', c_double)
|
||||
, ('julian_power', c_double)
|
||||
, ('julian_dist', c_double)
|
||||
, ('juliascope_power', c_double)
|
||||
, ('juliascope_dist', c_double)
|
||||
, ('radial_blur_angle', c_double)
|
||||
, ('pie_slices', c_double)
|
||||
, ('pie_rotation', c_double)
|
||||
, ('pie_thickness', c_double)
|
||||
, ('ngon_sides', c_double)
|
||||
, ('ngon_power', c_double)
|
||||
, ('ngon_circle', c_double)
|
||||
, ('ngon_corners', c_double)
|
||||
, ('curl_c1', c_double)
|
||||
, ('curl_c2', c_double)
|
||||
, ('rectangles_x', c_double)
|
||||
, ('rectangles_y', c_double)
|
||||
, ('amw_amp', c_double)
|
||||
, ('disc2_rot', c_double)
|
||||
, ('disc2_twist', c_double)
|
||||
, ('super_shape_rnd', c_double)
|
||||
, ('super_shape_m', c_double)
|
||||
, ('super_shape_n1', c_double)
|
||||
, ('super_shape_n2', c_double)
|
||||
, ('super_shape_n3', c_double)
|
||||
, ('super_shape_holes', c_double)
|
||||
, ('flower_petals', c_double)
|
||||
, ('flower_holes', c_double)
|
||||
, ('conic_eccentricity', c_double)
|
||||
, ('conic_holes', c_double)
|
||||
, ('parabola_height', c_double)
|
||||
, ('parabola_width', c_double)
|
||||
, ('bent2_x', c_double)
|
||||
, ('bent2_y', c_double)
|
||||
, ('bipolar_shift', c_double)
|
||||
, ('cell_size', c_double)
|
||||
, ('cpow_r', c_double)
|
||||
, ('cpow_i', c_double)
|
||||
, ('cpow_power', c_double)
|
||||
, ('curve_xamp', c_double)
|
||||
, ('curve_yamp', c_double)
|
||||
, ('curve_xlength', c_double)
|
||||
, ('curve_ylength', c_double)
|
||||
, ('escher_beta', c_double)
|
||||
, ('lazysusan_spin', c_double)
|
||||
, ('lazysusan_space', c_double)
|
||||
, ('lazysusan_twist', c_double)
|
||||
, ('lazysusan_x', c_double)
|
||||
, ('lazysusan_y', c_double)
|
||||
, ('modulus_x', c_double)
|
||||
, ('modulus_y', c_double)
|
||||
, ('oscope_separation', c_double)
|
||||
, ('oscope_frequency', c_double)
|
||||
, ('oscope_amplitude', c_double)
|
||||
, ('oscope_damping', c_double)
|
||||
, ('popcorn2_x', c_double)
|
||||
, ('popcorn2_y', c_double)
|
||||
, ('popcorn2_c', c_double)
|
||||
, ('separation_x', c_double)
|
||||
, ('separation_xinside', c_double)
|
||||
, ('separation_y', c_double)
|
||||
, ('separation_yinside', c_double)
|
||||
, ('split_xsize', c_double)
|
||||
, ('split_ysize', c_double)
|
||||
, ('splits_x', c_double)
|
||||
, ('splits_y', c_double)
|
||||
, ('stripes_space', c_double)
|
||||
, ('stripes_warp', c_double)
|
||||
, ('wedge_angle', c_double)
|
||||
, ('wedge_hole', c_double)
|
||||
, ('wedge_count', c_double)
|
||||
, ('wedge_swirl', c_double)
|
||||
, ('wedge_julia_angle', c_double)
|
||||
, ('wedge_julia_count', c_double)
|
||||
, ('wedge_julia_power', c_double)
|
||||
, ('wedge_julia_dist', c_double)
|
||||
, ('wedge_sph_angle', c_double)
|
||||
, ('wedge_sph_count', c_double)
|
||||
, ('wedge_sph_hole', c_double)
|
||||
, ('wedge_sph_swirl', c_double)
|
||||
, ('whorl_inside', c_double)
|
||||
, ('whorl_outside', c_double)
|
||||
, ('waves2_freqx', c_double)
|
||||
, ('waves2_scalex', c_double)
|
||||
, ('waves2_freqyx', c_double)
|
||||
, ('waves2_scaley', c_double)
|
||||
, ('auger_sym', c_double)
|
||||
, ('auger_weight', c_double)
|
||||
, ('auger_freq', c_double)
|
||||
, ('auger_scale', c_double)
|
||||
, ('flux_spread', c_double)
|
||||
, ('mobius_re_a', c_double)
|
||||
, ('mobius_im_a', c_double)
|
||||
, ('mobius_re_b', c_double)
|
||||
, ('mobius_im_b', c_double)
|
||||
, ('mobius_re_c', c_double)
|
||||
, ('mobius_im_c', c_double)
|
||||
, ('persp_vsin', c_double)
|
||||
, ('persp_vfcos', c_double)
|
||||
, ('julian_rN', c_double)
|
||||
, ('julian_cN', c_double)
|
||||
, ('juliascope_rN', c_double)
|
||||
, ('juliascope_cN', c_double)
|
||||
, ('wedgeJulia_rN', c_double)
|
||||
, ('wedgeJulia_cn', c_double)
|
||||
, ('wedgeJulia_cf', c_double)
|
||||
, ('radialBlur_spinvar', c_double)
|
||||
, ('radialBlur_zoomvar', c_double)
|
||||
, ('waves_dx2', c_double)
|
||||
, ('waves_dy2', c_double)
|
||||
, ('disc2_sinadd', c_double)
|
||||
, ('disc2_cosadd', c_double)
|
||||
, ('disc2_timespi', c_double)
|
||||
, ('super_shape_pm_4', c_double)
|
||||
, ('super_shape_pneg1_n1', c_double)
|
||||
, ('num_active_vars', c_int)
|
||||
, ('active_var_weights', c_double * flam3_nvariations)
|
||||
, ('varFunc', c_int * flam3_nvariations)
|
||||
, ('motion_freq', c_int)
|
||||
, ('motion_func', c_int)
|
||||
, ('motion', POINTER(BaseXForm))
|
||||
, ('num_motion', c_int)
|
||||
# It seems I'm missing something in the current version.
|
||||
, ('mysterious_padding', c_double * 2) ]
|
||||
|
@ -130,7 +130,7 @@ void iter(mwc_st *msts, iter_info *infos, float *accbuf, float *denbuf) {
|
||||
packer = self.packer.view('info'))
|
||||
|
||||
|
||||
def silly(features, cps):
|
||||
def render(features, cps):
|
||||
nsteps = 1000
|
||||
abuf = np.zeros((1024, 1024, 4), dtype=np.float32)
|
||||
dbuf = np.zeros((1024, 1024), dtype=np.float32)
|
||||
|
@ -35,7 +35,7 @@ var(5, 'polar', """
|
||||
|
||||
var(10, 'hyperbolic', """
|
||||
float a = atan2f(tx, ty);
|
||||
float r = sqrt(tx*tx + ty*ty) + 1e-20;
|
||||
float r = sqrt(tx*tx + ty*ty) + 1e-20f;
|
||||
ox += w * sinf(a) / r;
|
||||
oy += w * cosf(a) * r;
|
||||
""")
|
||||
@ -46,7 +46,7 @@ var(33, 'juliascope', """
|
||||
float t_rnd = truncf(mwc_next_01(rctx) * fabsf(power));
|
||||
// TODO: don't draw the extra random number
|
||||
if (mwc_next(rctx) & 1) ang = -ang;
|
||||
float tmpr = (2 * M_PI * t_rnd + ang) / power;
|
||||
float tmpr = (2.0f * M_PI * t_rnd + ang) / power;
|
||||
|
||||
float cn = {{px.get('xf.juliascope_dist / xf.juliascope_power / 2',
|
||||
'juscope_cn')}};
|
||||
|
159
cuburn/render.py
159
cuburn/render.py
@ -26,103 +26,6 @@ class Genome(pyflam3.Genome):
|
||||
dens /= np.sum(dens)
|
||||
self.norm_density = [np.sum(dens[:i+1]) for i in range(len(dens))]
|
||||
|
||||
class XForm(object):
|
||||
"""
|
||||
A Python structure (*not* a ctypes wrapper) storing an xform. There are
|
||||
a few differences between the meaning of properties on this object and
|
||||
those of the C version; they are noted below.
|
||||
"""
|
||||
def __init__(self, **kwargs):
|
||||
read_affine = lambda c: [[c[0], c[1]], [c[2], c[3]], [c[4], c[5]]]
|
||||
self.coefs = read_affine(map(float, kwargs.pop('coefs').split()))
|
||||
if 'post' in kwargs:
|
||||
self.post = read_affine(map(float, kwargs.pop('post').split()))
|
||||
# TODO: more verification, detection of unknown variables, etc
|
||||
for k, v in kwargs.items():
|
||||
setattr(self, k, float(v))
|
||||
|
||||
# ctypes was being a pain. just parse the string.
|
||||
@classmethod
|
||||
def parse(cls, cp):
|
||||
flame_str = flam3_print_to_string(byref(cp))
|
||||
xforms = []
|
||||
for line in flame_str.split('\n'):
|
||||
if not line.strip().startswith('<xform'):
|
||||
continue
|
||||
props = dict(re.findall(r'(\w*)="([^"]*)"', line))
|
||||
xforms.append(cls(**props))
|
||||
# Set cumulative xform weight
|
||||
xforms[0].cweight = xforms[0].weight
|
||||
for i in range(1, len(xforms)):
|
||||
xforms[i].cweight = xforms[i].weight + xforms[i-1].cweight
|
||||
xforms[-1].cweight = 1.0
|
||||
return xforms
|
||||
|
||||
class _Frame(pyflam3.Frame):
|
||||
"""
|
||||
ctypes flam3_frame object used for genome interpolation and
|
||||
spatial filter creation
|
||||
"""
|
||||
def __init__(self, genomes, *args, **kwargs):
|
||||
pyflam3.Frame.__init__(self, *args, **kwargs)
|
||||
self.genomes = (BaseGenome * len(genomes))()
|
||||
for i in range(len(genomes)):
|
||||
memmove(byref(self.genomes[i]), byref(genomes[i]),
|
||||
sizeof(BaseGenome))
|
||||
self.ngenomes = len(genomes)
|
||||
|
||||
# TODO: allow user to override this
|
||||
self.pixel_aspect_ratio = 1.0
|
||||
|
||||
def interpolate(self, time, stagger=0, cp=None):
|
||||
cp = cp or BaseGenome()
|
||||
flam3_interpolate(self.genomes, self.ngenomes, time,
|
||||
stagger, byref(cp))
|
||||
return cp
|
||||
|
||||
class Frame(object):
|
||||
"""
|
||||
Handler for a single frame of a rendered genome.
|
||||
"""
|
||||
def __init__(self, _frame, time):
|
||||
self._frame = _frame
|
||||
self.center_cp = self._frame.interpolate(time)
|
||||
|
||||
def upload_data(self, ctx, filters, time):
|
||||
"""
|
||||
Prepare and upload the data needed to render this frame to the device.
|
||||
"""
|
||||
center = self.center_cp
|
||||
ncps = center.nbatches * center.ntemporal_samples
|
||||
|
||||
# TODO: isn't this leaking xforms from C all over the place?
|
||||
stream = StringIO()
|
||||
cp_list = []
|
||||
|
||||
for batch_idx in range(center.nbatches):
|
||||
for time_idx in range(center.ntemporal_samples):
|
||||
idx = time_idx + batch_idx * center.nbatches
|
||||
interp_time = time + filters.temporal_deltas[idx]
|
||||
cp = self._frame.interpolate(interp_time)
|
||||
cp_list.append(cp)
|
||||
|
||||
cp.camera = Camera(self._frame, cp, filters)
|
||||
cp.nsamples = (cp.camera.sample_density *
|
||||
center.width * center.height) / ncps
|
||||
|
||||
|
||||
print "Expected writes:", (
|
||||
cp.camera.sample_density * center.width * center.height)
|
||||
min_time = min(filters.temporal_deltas)
|
||||
max_time = max(filters.temporal_deltas)
|
||||
for i, cp in enumerate(cp_list):
|
||||
cp.norm_time = (filters.temporal_deltas[i] - min_time) / (
|
||||
max_time - min_time)
|
||||
CPDataStream.pack_into(ctx, stream, frame=self, cp=cp, cp_idx=idx)
|
||||
PaletteLookup.upload_palette(ctx, self, cp_list)
|
||||
stream.seek(0)
|
||||
IterThread.upload_cp_stream(ctx, stream.read(), ncps)
|
||||
|
||||
class Animation(object):
|
||||
"""
|
||||
Control structure for rendering a series of frames.
|
||||
@ -142,57 +45,13 @@ class Animation(object):
|
||||
interpolated sequence between one or two genomes.
|
||||
"""
|
||||
def __init__(self, genomes, ngenomes = None):
|
||||
# _frame is the ctypes frame object used only for interpolation
|
||||
self._frame = _Frame(genomes)
|
||||
|
||||
# Use the same set of filters throughout the anim, a la flam3
|
||||
self.filters = Filters(self._frame, genomes[0])
|
||||
self.features = Features(genomes, self.filters)
|
||||
self.features = Features(genomes)
|
||||
|
||||
def compile(self):
|
||||
pass
|
||||
def render_frame(self, time=0):
|
||||
pass
|
||||
|
||||
class Filters(object):
|
||||
def __init__(self, frame, cp):
|
||||
# Use one oversample per filter set, even over multiple timesteps
|
||||
self.oversample = frame.genomes[0].spatial_oversample
|
||||
|
||||
# Ugh. I'd really like to replace this mess
|
||||
spa_filt_ptr = POINTER(c_double)()
|
||||
spa_width = flam3_create_spatial_filter(byref(frame),
|
||||
flam3_field_both,
|
||||
byref(spa_filt_ptr))
|
||||
if spa_width < 0:
|
||||
raise EnvironmentError("flam3 call failed")
|
||||
self.spatial = np.asarray([[spa_filt_ptr[y*spa_width+x] for x in
|
||||
range(spa_width)] for y in range(spa_width)], dtype=np.double)
|
||||
self.spatial_width = spa_width
|
||||
flam3_free(spa_filt_ptr)
|
||||
|
||||
tmp_filt_ptr = POINTER(c_double)()
|
||||
tmp_deltas_ptr = POINTER(c_double)()
|
||||
steps = cp.nbatches * cp.ntemporal_samples
|
||||
self.temporal_sum = flam3_create_temporal_filter(
|
||||
steps,
|
||||
cp.temporal_filter_type,
|
||||
cp.temporal_filter_exp,
|
||||
cp.temporal_filter_width,
|
||||
byref(tmp_filt_ptr),
|
||||
byref(tmp_deltas_ptr))
|
||||
self.temporal = np.asarray([tmp_filt_ptr[i] for i in range(steps)],
|
||||
dtype=np.double)
|
||||
flam3_free(tmp_filt_ptr)
|
||||
self.temporal_deltas = np.asarray(
|
||||
[tmp_deltas_ptr[i] for i in range(steps)], dtype=np.double)
|
||||
flam3_free(tmp_deltas_ptr)
|
||||
|
||||
# TODO: density estimation
|
||||
self.gutter = (spa_width - self.oversample) / 2
|
||||
|
||||
|
||||
|
||||
class Features(object):
|
||||
"""
|
||||
Determine features and constants required to render a particular set of
|
||||
@ -212,7 +71,7 @@ class Features(object):
|
||||
# performance too much. Power-of-two, please.
|
||||
palette_height = 16
|
||||
|
||||
def __init__(self, genomes, flt):
|
||||
def __init__(self, genomes):
|
||||
any = lambda l: bool(filter(None, map(l, genomes)))
|
||||
self.max_ntemporal_samples = max(
|
||||
[cp.nbatches * cp.ntemporal_samples for cp in genomes])
|
||||
@ -227,15 +86,6 @@ class Features(object):
|
||||
if any(lambda cp: cp.final_xform_enable):
|
||||
raise NotImplementedError("Final xform")
|
||||
|
||||
# Histogram (and log-density copy) width and height
|
||||
self.hist_width = flt.oversample * genomes[0].width + 2 * flt.gutter
|
||||
self.hist_height = flt.oversample * genomes[0].height + 2 * flt.gutter
|
||||
# Histogram stride, for better filtering. This code assumes the
|
||||
# 128-byte L1 cache line width of Fermi devices, and a 16-byte
|
||||
# histogram bucket size. TODO: detect these things programmatically,
|
||||
# particularly the histogram bucket size, which may be split soon
|
||||
self.hist_stride = 8 * int(math.ceil(self.hist_width / 8.0))
|
||||
|
||||
class XFormFeatures(object):
|
||||
def __init__(self, xforms, xform_id):
|
||||
self.id = xform_id
|
||||
@ -248,7 +98,7 @@ class XFormFeatures(object):
|
||||
|
||||
class Camera(object):
|
||||
"""Viewport and exposure."""
|
||||
def __init__(self, frame, cp, filters):
|
||||
def __init__(self, frame, cp):
|
||||
# Calculate the conversion matrix between the IFS space (xform
|
||||
# coordinates) and the sampling lattice (bucket addresses)
|
||||
# TODO: test this code (against compute_camera?)
|
||||
@ -262,8 +112,6 @@ class Camera(object):
|
||||
self.ppu = Point(
|
||||
cp.pixels_per_unit * scale / frame.pixel_aspect_ratio,
|
||||
cp.pixels_per_unit * scale)
|
||||
# extra shifts applied due to gutter
|
||||
gutter = filters.gutter / (cp.spatial_oversample * self.ppu)
|
||||
cornerLL = center - (size / (2 * self.ppu))
|
||||
self.lower_bounds = cornerLL - gutter
|
||||
self.upper_bounds = cornerLL + (size / self.ppu) + gutter
|
||||
@ -271,3 +119,4 @@ class Camera(object):
|
||||
self.norm_offset = -self.norm_scale * self.lower_bounds
|
||||
self.idx_scale = size * self.norm_scale
|
||||
self.idx_offset = size * self.norm_offset
|
||||
|
||||
|
30
main.py
30
main.py
@ -11,7 +11,7 @@
|
||||
|
||||
import os
|
||||
import sys
|
||||
os.environ['PATH'] = '/usr/x86_64-pc-linux-gnu/gcc-bin/4.4.5:' + os.environ['PATH']
|
||||
|
||||
from pprint import pprint
|
||||
from ctypes import *
|
||||
|
||||
@ -19,28 +19,38 @@ import numpy as np
|
||||
np.set_printoptions(precision=5, edgeitems=20)
|
||||
import scipy
|
||||
|
||||
import pyglet
|
||||
import pycuda.autoinit
|
||||
|
||||
from fr0stlib.pyflam3 import *
|
||||
from fr0stlib.pyflam3._flam3 import *
|
||||
|
||||
import pyglet
|
||||
window = pyglet.window.Window(1024, 1024)
|
||||
import pycuda.gl.autoinit
|
||||
|
||||
import cuburn._pyflam3_hacks
|
||||
from cuburn.render import *
|
||||
from cuburn.code.mwc import MWCTest
|
||||
from cuburn.code.iter import silly, membench
|
||||
from cuburn.code.iter import render, membench
|
||||
|
||||
# Required on my system; CUDA doesn't yet work with GCC 4.5
|
||||
os.environ['PATH'] = ('/usr/x86_64-pc-linux-gnu/gcc-bin/4.4.5:'
|
||||
+ os.environ['PATH'])
|
||||
|
||||
def main(args):
|
||||
#MWCTest.test_mwc()
|
||||
with open(args[-1]) as fp:
|
||||
if '-t' in args:
|
||||
MWCTest.test_mwc()
|
||||
membench()
|
||||
|
||||
window = pyglet.window.Window(1024, 1024) if '-g' in args else None
|
||||
|
||||
with open(args[1]) as fp:
|
||||
genomes = Genome.from_string(fp.read())
|
||||
anim = Animation(genomes)
|
||||
accum, den = silly(anim.features, genomes)
|
||||
accum, den = render(anim.features, genomes)
|
||||
|
||||
noalpha = np.delete(accum, 3, axis=2)
|
||||
scipy.misc.imsave('rendered.png', noalpha)
|
||||
|
||||
|
||||
|
||||
if '-g' not in args:
|
||||
return
|
||||
|
||||
@ -66,7 +76,7 @@ def main(args):
|
||||
pyglet.app.run()
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2 or not os.path.isfile(sys.argv[-1]):
|
||||
if len(sys.argv) < 2 or not os.path.isfile(sys.argv[1]):
|
||||
print "Last argument must be a path to a genome file"
|
||||
sys.exit(1)
|
||||
main(sys.argv)
|
||||
|
Loading…
Reference in New Issue
Block a user