Add xforms and variations.

This commit is contained in:
Steven Robertson 2010-09-11 13:10:41 -04:00
parent 383c0f1f9a
commit 860d7b2fad
2 changed files with 188 additions and 1 deletions

View File

@ -1,4 +1,6 @@
import sys
import math import math
import re
from ctypes import * from ctypes import *
from cStringIO import StringIO from cStringIO import StringIO
import numpy as np import numpy as np
@ -9,12 +11,45 @@ from fr0stlib.pyflam3.constants import *
from cuburn.cuda import LaunchContext from cuburn.cuda import LaunchContext
from cuburn.device_code import * from cuburn.device_code import *
from cuburn.variations import Variations
Point = lambda x, y: np.array([x, y], dtype=np.double) Point = lambda x, y: np.array([x, y], dtype=np.double)
class Genome(pyflam3.Genome): class Genome(pyflam3.Genome):
pass pass
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): class _Frame(pyflam3.Frame):
""" """
ctypes flam3_frame object used for genome interpolation and ctypes flam3_frame object used for genome interpolation and
@ -56,7 +91,7 @@ class Frame(object):
raise NotImplementedError( raise NotImplementedError(
"Distribution of a CP across multiple CTAs not yet done") "Distribution of a CP across multiple CTAs not yet done")
# TODO: isn't this leaking ctypes xforms all over the place? # TODO: isn't this leaking xforms from C all over the place?
stream = StringIO() stream = StringIO()
cp_list = [] cp_list = []
@ -70,6 +105,7 @@ class Frame(object):
cp.camera = Camera(self._frame, cp, filters) cp.camera = Camera(self._frame, cp, filters)
cp.nsamples = (cp.camera.sample_density * cp.nsamples = (cp.camera.sample_density *
center.width * center.height) / ncps center.width * center.height) / ncps
cp.xforms = XForm.parse(cp)
print "Expected writes:", ( print "Expected writes:", (
cp.camera.sample_density * center.width * center.height) cp.camera.sample_density * center.width * center.height)
@ -170,6 +206,8 @@ class Filters(object):
# TODO: density estimation # TODO: density estimation
self.gutter = (spa_width - self.oversample) / 2 self.gutter = (spa_width - self.oversample) / 2
class Features(object): class Features(object):
""" """
Determine features and constants required to render a particular set of Determine features and constants required to render a particular set of
@ -186,6 +224,14 @@ class Features(object):
self.non_box_temporal_filter = genomes[0].temporal_filter_type self.non_box_temporal_filter = genomes[0].temporal_filter_type
self.palette_mode = genomes[0].palette_mode and "linear" or "nearest" self.palette_mode = genomes[0].palette_mode and "linear" or "nearest"
xforms = [XForm.parse(cp) for cp in genomes]
assert len(xforms[0]) == len(xforms[-1]), ("genomes must have same "
"number of xforms! (try running through flam3-genome first)")
self.xforms = [XFormFeatures([x[i] for x in xforms], i)
for i in range(len(xforms[0]))]
if any(lambda cp: cp.final_xform_enable):
raise NotImplementedError("Final xform")
# Histogram (and log-density copy) width and height # Histogram (and log-density copy) width and height
self.hist_width = flt.oversample * genomes[0].width + 2 * flt.gutter self.hist_width = flt.oversample * genomes[0].width + 2 * flt.gutter
self.hist_height = flt.oversample * genomes[0].height + 2 * flt.gutter self.hist_height = flt.oversample * genomes[0].height + 2 * flt.gutter
@ -195,6 +241,14 @@ class Features(object):
# particularly the histogram bucket size, which may be split soon # particularly the histogram bucket size, which may be split soon
self.hist_stride = 8 * int(math.ceil(self.hist_width / 8.0)) 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
any = lambda l: bool(filter(None, map(l, xforms)))
self.has_post = any(lambda xf: getattr(xf, 'post', None))
self.vars = set([n for x in xforms for n in Variations.names
if getattr(x, n, None)])
class Camera(object): class Camera(object):
"""Viewport and exposure.""" """Viewport and exposure."""
def __init__(self, frame, cp, filters): def __init__(self, frame, cp, filters):

133
cuburn/variations.py Normal file
View File

@ -0,0 +1,133 @@
from cuburn.ptx import PTXFragment, ptx_func
class Variations(PTXFragment):
"""
You know it.
"""
# TODO: precalc
shortname = "variations"
def __init__(self):
self.xform_idx = None
names = [ "linear", "sinusoidal", "spherical", "swirl", "horseshoe",
"polar", "handkerchief", "heart", "disc", "spiral", "hyperbolic",
"diamond", "ex", "julia", "bent", "waves", "fisheye", "popcorn",
"exponential", "power", "cosine", "rings", "fan", "blob", "pdj",
"fan2", "rings2", "eyefish", "bubble", "cylinder", "perspective",
"noise", "julian", "juliascope", "blur", "gaussian_blur",
"radial_blur", "pie", "ngon", "curl", "rectangles", "arch", "tangent",
"square", "rays", "blade", "secant2", "twintrian", "cross", "disc2",
"super_shape", "flower", "conic", "parabola", "bent2", "bipolar",
"boarders", "butterfly", "cell", "cpow", "curve", "edisc", "elliptic",
"escher", "foci", "lazysusan", "loonie", "pre_blur", "modulus",
"oscilloscope", "polar2", "popcorn2", "scry", "separation", "split",
"splits", "stripes", "wedge", "wedge_julia", "wedge_sph", "whorl",
"waves2", "exp", "log", "sin", "cos", "tan", "sec", "csc", "cot",
"sinh", "cosh", "tanh", "sech", "csch", "coth", "auger", "flux", ]
@ptx_func
def xfg(self, dst, expr):
"""
Convenience wrapper around cp.get which loads the given property from
the current CP and XF.
"""
# xform_idx is set by apply_xform on the current instance, but the
# expression will be evaluated using each CP in stream packing.
cp.get(cpA, dst, 'cp.xforms[%d].%s' % (self.xform_idx, expr))
@ptx_func
def xfg_v2(self, dst1, expr1, dst2, expr2):
cp.get_v2(cpA, dst1, 'cp.xforms[%d].%s' % (self.xform_idx, expr1),
dst2, 'cp.xforms[%d].%s' % (self.xform_idx, expr2))
@ptx_func
def xfg_v4(self, d1, e1, d2, e2, d3, e3, d4, e4):
cp.get_v4(cpA, d1, 'cp.xforms[%d].%s' % (self.xform_idx, e1),
d2, 'cp.xforms[%d].%s' % (self.xform_idx, e2),
d3, 'cp.xforms[%d].%s' % (self.xform_idx, e3),
d4, 'cp.xforms[%d].%s' % (self.xform_idx, e4))
@ptx_func
def apply_xform(self, xo, yo, co, xi, yi, ci, xform_idx):
"""
Apply a transform.
This function makes a copy of the input variables, so it's safe to use
the same registers for input and output.
"""
with block("Apply xform %d" % xform_idx):
self.xform_idx = xform_idx
with block('Modify color'):
reg.f32('c_speed c_new')
cp.get_v2(cpA,
c_speed, '(1.0 - cp.xforms[%d].color_speed)' % xform_idx,
c_new, 'cp.xforms[%d].color * cp.xforms[%d].color_speed' %
(xform_idx, xform_idx))
op.fma.rn.ftz.f32(co, ci, c_speed, c_new)
reg.f32('xt yt')
with block("Do affine transformation"):
# TODO: verify that this is the best performance (register
# usage vs number of loads)
reg.f32('c00 c10 c20 c01 c11 c21')
self.xfg_v4(c00, 'coefs[0][0]', c01, 'coefs[0][1]',
c20, 'coefs[2][0]', c21, 'coefs[2][1]')
op.fma.rn.ftz.f32(xt, c00, xi, c20)
op.fma.rn.ftz.f32(yt, c01, xi, c21)
self.xfg_v2(c10, 'coefs[1][0]', c11, 'coefs[1][1]')
op.fma.rn.ftz.f32(xt, c10, yi, xt)
op.fma.rn.ftz.f32(yt, c11, yi, yt)
op.mov.f32(xo, '0.0')
op.mov.f32(yo, '0.0')
for var_name in sorted(features.xforms[xform_idx].vars):
func = getattr(self, var_name, None)
if not func:
raise NotImplementedError(
"Haven't implemented %s yet" % var_name)
with block('%s variation' % var_name):
reg.f32('wgt')
self.xfg(wgt, var_name)
func(xo, yo, xt, yt, wgt)
if features.xforms[xform_idx].has_post:
with block("Affine post-transformation"):
op.mov.f32(xt, xo)
op.mov.f32(yt, yo)
reg.f32('c00 c10 c20 c01 c11 c21')
self.xfg_v4(c00, 'post[0][0]', c01, 'post[0][1]',
c20, 'post[2][0]', c21, 'post[2][1]')
op.fma.rn.ftz.f32(xo, c00, xt, c20)
op.fma.rn.ftz.f32(yo, c01, xt, c21)
self.xfg_v2(c10, 'post[1][0]', c11, 'post[1][1]')
op.fma.rn.ftz.f32(xo, c10, yt, xo)
op.fma.rn.ftz.f32(yo, c11, yt, yo)
@ptx_func
def linear(self, xo, yo, xi, yi, wgt):
op.fma.rn.ftz.f32(xo, xi, wgt, xo)
op.fma.rn.ftz.f32(yo, yi, wgt, xo)
@ptx_func
def sinusoidal(self, xo, yo, xi, yi, wgt):
reg.f32('sinval')
op.sin.approx.ftz.f32(sinval, xi)
op.fma.rn.ftz.f32(xo, sinval, wgt, xo)
op.sin.approx.ftz.f32(sinval, yi)
op.fma.rn.ftz.f32(yo, sinval, wgt, yo)
@ptx_func
def spherical(self, xo, yo, xi, yi, wgt):
reg.f32('r2')
op.fma.rn.ftz.f32(r2, xi, xi, '1e-9')
op.fma.rn.ftz.f32(r2, yi, yi, r2)
op.rcp.approx.f32(r2, r2)
op.mul.rn.ftz.f32(r2, r2, wgt)
op.fma.rn.ftz.f32(xo, xi, r2, xo)
op.fma.rn.ftz.f32(yo, yi, r2, yo)