Compare commits

...

4 commits

Author SHA1 Message Date
Jeff Epler
8400a0a4ba clean up 2017-06-05 21:36:36 -05:00
Jeff Epler
0f131b2363 Use texturing to decrease number of segments plotted 2017-06-03 10:35:47 -05:00
Jeff Epler
dc0a84f102 WIP work on intel / debian stretch 2017-06-02 21:07:27 -05:00
Jeff Epler
055988dc03 WIP 2017-06-02 20:42:15 -05:00
7 changed files with 510 additions and 18 deletions

View file

@ -1,13 +1,33 @@
.PHONY: default
default: dashing
.PHONY: bench
bench: dashing
perf stat --log-fd=2 ./dashing -b -s .1 data/HWOOD6E1.pat data/sf.seg
.PHONY: bench%
bench%: dashing%
perf stat --log-fd=2 ./$< -b -s .1 data/HWOOD6E1.pat data/sf.seg
dashing: main.cc dashing.cc dashing.hh parse_numbers.hh contours_and_segments.hh
g++ -W -Wall -O2 -g -std=c++11 $(filter %.cc, $^) -o $@ -lboost_random
dashing-noopt: main.cc dashing.cc dashing.hh parse_numbers.hh contours_and_segments.hh
g++ -W -Wall -O0 -g -std=c++11 $(filter %.cc, $^) -o $@ -lboost_random
dashing-clang: main.cc dashing.cc dashing.hh parse_numbers.hh
clang++ -W -Wall -O2 -g -std=c++11 $(filter %.cc, $^) -o $@ -lboost_random
HEADERS:
dashing: main.o dashing.o
g++ -W -Wall -O2 -g -std=c++11 $^ -o $@
dashing%: main%.o dashing%.o
g++ -W -Wall -O2 -g -std=c++11 $^ -o $@
HEADERS := $(wildcard *.hh)
default: gldashing
gldashing: glmain.o dashing.o glsetup.o
g++ -o $@ $^ -lSDL2 -lGLEW -lGL -lrt
gldashing%: glmain%.o dashing%.o glsetup%.o
g++ -o $@ $^ -lSDL2 -lGLEW -lGL -lrt
%.o: %.cc $(HEADERS)
g++ -W -Wall -O2 -g -std=c++11 -c $< -o $@
%-noopt.o: %.cc $(HEADERS)
g++ -W -Wall -O0 -g -std=c++11 -c $< -o $@
%-clang.o: %.cc $(HEADERS)
clang++ -W -Wall -O0 -g -std=c++11 -c $< -o $@
.PHONY: clean
clean:
rm -f dashing dashing-noopt dashing-clang \
gldashing gldashing-noopt gldashing-clang \
*.o *.d

View file

@ -1,3 +1,5 @@
#pragma once
#include "dashing.hh"
#include "parse_numbers.hh"
#include <random>
@ -10,7 +12,7 @@ typedef std::vector<Segment> Segments;
typedef std::vector<Point> Contour;
typedef std::vector<Contour> Contours;
void ContourToSegments(Segments &dest,
inline void ContourToSegments(Segments &dest,
/* EXPLICIT COPY */ Contour src,
double jitter = 0)
{
@ -32,7 +34,7 @@ void ContourToSegments(Segments &dest,
dest.emplace_back(Segment{src[i-1], src[0], false});
}
void ContoursToSegments(Segments &dest, Contours const &src, double jitter=0)
inline void ContoursToSegments(Segments &dest, Contours const &src, double jitter=0)
{
dest.clear();
@ -42,7 +44,7 @@ void ContoursToSegments(Segments &dest, Contours const &src, double jitter=0)
}
}
Contours ContoursFromFile(std::istream &fi)
inline Contours ContoursFromFile(std::istream &fi)
{
auto result = Contours{};
auto line = std::string{};
@ -62,14 +64,20 @@ Contours ContoursFromFile(std::istream &fi)
return result;
}
Segments SegmentsFromFile(std::istream &fi, double jitter) {
inline Contours ContoursFromFile(const char *filename)
{
std::fstream fi(filename);
return ContoursFromFile(fi);
}
inline Segments SegmentsFromFile(std::istream &fi, double jitter) {
auto && contours = ContoursFromFile(fi);
auto result = Segments{};
ContoursToSegments(result, contours, jitter);
return result;
}
std::vector<Segment> SegmentsFromFile(const char *filename, double jitter) {
inline std::vector<Segment> SegmentsFromFile(const char *filename, double jitter) {
std::fstream fi(filename);
return SegmentsFromFile(fi, jitter);
}

View file

@ -18,8 +18,7 @@ freely, subject to the following restrictions:
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef DASHING_H
#define DASHING_H
#pragma once
#include <cmath>
#include <cassert>
@ -44,6 +43,7 @@ PSMatrix Translation(double x, double y);
PSMatrix Rotation(double theta);
PSMatrix XSkew(double xk);
PSMatrix YScale(double ys);
PSMatrix Scale(double s);
struct Point { double x, y; };
inline Point operator*(const Point &p, const PSMatrix &m) {
@ -244,4 +244,3 @@ void xyhatch(const HatchPattern &pattern, const C &c, Cb cb, Wr wr) {
}
}
#endif

23
gldashing.hh Normal file
View file

@ -0,0 +1,23 @@
#include "dashing.hh"
#include "contours_and_segments.hh"
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm/gtc/type_precision.hpp>
#include <glm/gtx/transform.hpp>
void setup();
struct AppState
{
double jitter, scale;
const char *rule;
int attribute_aPos, attribute_aColor, uniform_uXf, uniform_uTex;
unsigned texture;
glm::fmat4x4 mat;
dashing::Contours c;
dashing::HatchPattern h;
};
extern AppState st;

300
glmain.cc Normal file
View file

@ -0,0 +1,300 @@
/*
Copyright (c) 2015 Jeff Epler
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgement in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
// demo and benchmark program for dashing
#include "dashing.hh"
#include "contours_and_segments.hh"
#include <SDL2/SDL.h>
#include "gldashing.hh"
using namespace dashing;
AppState st;
const char *argv0 = "dashing";
#ifdef __GNUC__
void usage() __attribute__((noreturn));
#endif
void usage() {
fprintf(stderr,
"Usage: %s [-b] [-s scale] [-j jitter] [-r rulename] patfile segfile\n",
argv0);
exit(1);
}
template<class C, class Cb>
void xyhatch(const HatchPattern &pattern, const C &c, Cb cb, const char *arg) {
if(!strcmp(arg, "odd"))
return xyhatch(pattern, c, cb, [](int i) { return i % 2 != 0; });
if(!strcmp(arg, "nonzero"))
return xyhatch(pattern, c, cb, [](int i) { return i != 0; });
if(!strcmp(arg, "positive"))
return xyhatch(pattern, c, cb, [](int i) { return i > 0; });
if(!strcmp(arg, "negative"))
return xyhatch(pattern, c, cb, [](int i) { return i < 0; });
if(!strcmp(arg, "abs_geq_two"))
return xyhatch(pattern, c, cb, [](int i) { return abs(i) >= 2; });
fprintf(stderr, "Unrecognized winding rule '%s'\n", arg);
fprintf(stderr, "Rules are: odd nonzero positive negative abs_geq_two\n");
usage();
}
template<class Cb, class Wr>
void glspans(std::vector<Segment> && segments, Cb cb, std::vector<Intersection> &uu, Wr wr) {
if(segments.empty()) return; // no segments
for(auto &s : segments) ysort(s);
std::sort(segments.begin(), segments.end(),
[](const Segment &a, const Segment &b) {
return a.p.y < b.p.y; // sort in increasing p.y
});
// we want to maintain the heap condition in such a way that we can always
// quickly pop items that our span has moved past.
// C++ heaps are max-heaps, so we need a decreasing sort.
auto heapcmp = [](const Segment &a, const Segment &b) {
return b.q.y < a.q.y; // sort in decreasing q.y;
};
auto segments_begin = segments.begin();
auto heap_begin = segments.begin(), heap_end = segments.begin();
auto vstart = intfloor(segments.front().p.y);
auto vend = intceil(std::max_element(segments.begin(), segments.end(),
[](const Segment &a, const Segment &b) {
return a.q.y < b.q.y; // sort in increasing q.y;
})->q.y);
// sweep-line algorithm to intersects spans with segments
// "active" holds segments that may intersect with this span;
// when v moves below an active segment, drop it from the active heap.
// when v moves into a remaining segment, move it from segments to active.
for(auto v = vstart; v != vend; v++) {
uu.clear();
while(heap_begin != heap_end && heap_begin->q.y < v)
{
std::pop_heap(heap_begin, heap_end, heapcmp);
heap_end --;
}
while(segments_begin != segments.end() && segments_begin->p.y < v) {
const auto &s = *segments_begin;
if(s.q.y >= v) {
*heap_end++ = s;
std::push_heap(heap_begin, heap_end, heapcmp);
}
segments_begin ++;
}
for(const auto &s : boost::make_iterator_range(heap_begin, heap_end)) {
auto du = s.q.x - s.p.x;
auto dv = s.q.y - s.p.y;
assert(dv);
if(dv) uu.push_back(
Intersection{s.p.x + du * (v - s.p.y) / dv,s.swapped});
}
std::sort(uu.begin(), uu.end());
int winding = 0;
double old_u = -std::numeric_limits<double>::infinity();
for(const auto &isect : uu) {
if(wr(winding)) cb(v, old_u, isect.u);
winding += 2*isect.positive - 1;
old_u = isect.u;
}
}
}
template<class It, class Cb>
void glhatch(int i, const Dash &pattern, It start, It end, Cb cb, std::vector<Segment> &uvsegments, std::vector<Intersection> &uu) {
uvsegments.clear();
bool swapped = pattern.tf.determinant() < 0;
std::transform(start, end, std::back_inserter(uvsegments),
[&](const Segment &s)
{ return Segment{s.p * pattern.tf, s.q * pattern.tf, swapped != s.swapped };
});
auto recip = 1./pattern.sum.back();
glspans(std::move(uvsegments), [&](double v, double u1, double u2) {
auto p = Point{u1, v} * pattern.tr;
auto q = Point{u2, v} * pattern.tr;
cb(i, p.x, p.y, u1*recip, q.x, q.y, u2*recip);
}, uu, [](int i) { return !!i; } );
}
template<class It, class Cb>
void glhatch(const HatchPattern &pattern, It start, It end, Cb cb) {
std::vector<Segment> uvsegments;
uvsegments.reserve(end-start);
std::vector<Intersection> uu;
uu.reserve(8);
for(size_t i=0; i<pattern.d.size(); i++)
glhatch(i, pattern.d[i], start, end, cb, uvsegments, uu);
}
template<class C, class Cb>
void glhatch(const HatchPattern &pattern, const C &c, Cb cb) {
glhatch(pattern, std::begin(c), std::end(c), cb);
}
size_t render(SDL_Window *window __attribute__((unused)))
{
glClear(GL_COLOR_BUFFER_BIT);
static Segments s;
ContoursToSegments(s, st.c, st.jitter);
static std::vector<float> p;
p.clear();
for(auto && si : s)
{
p.push_back(si.p.x);
p.push_back(si.p.y);
p.push_back(0.f);
p.push_back(-1);
p.push_back(si.q.x);
p.push_back(si.q.y);
p.push_back(0.f);
p.push_back(-1);
}
// draw outline of shape
glVertexAttrib3f(st.attribute_aColor, 0., 0., 0.);
glVertexAttribPointer(st.attribute_aPos, 4, GL_FLOAT, GL_FALSE,
0, &p[0]);
glDrawArrays(GL_LINES, 0, p.size()/4);
p.clear();
auto scale = pow(1.25, st.scale);
auto sf = PSMatrix{scale, 0., 0., scale, 0., 0.};
auto sr = PSMatrix{1./scale, 0., 0., 1./scale, 0., 0.};
auto h = st.h;
for(auto & i : h.d)
{
i.tf = i.tf * sf;
i.tr = sr * i.tr;
}
double frac = 1.*h.d.size();
glhatch(h, s, [frac](int i, double x1, double y1, double u1, double x2, double y2, double u2) {
p.push_back(x1);
p.push_back(y1);
p.push_back(u1);
p.push_back(i);
p.push_back(x2);
p.push_back(y2);
p.push_back(u2);
p.push_back(i);
});
// draw dashes of shape
glVertexAttrib3f(st.attribute_aColor, 0., 0., 1.);
glVertexAttribPointer(st.attribute_aPos, 4, GL_FLOAT, GL_FALSE,
0, &p[0]);
glDrawArrays(GL_LINES, 0, p.size()/4);
SDL_GL_SwapWindow(window);
return p.size() / 16;
}
double now()
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec + ts.tv_nsec * 1e-9;
}
void mainloop(SDL_Window *window)
{
SDL_Event ev;
auto t0 = now();
while(1) {
while(SDL_PollEvent(&ev)) {
if(ev.type == SDL_QUIT) return;
if(ev.type == SDL_KEYDOWN)
{
if(ev.key.keysym.sym == SDLK_LEFT) st.scale -= 1;
if(ev.key.keysym.sym == SDLK_RIGHT) st.scale += 1;
}
}
auto n = render(window);
auto t1 = now();
printf("scale = %5.2f frame time = %6.4f [est fps=%4.1f] segments=%zd [est Mseg/s=%5.1f]\n", pow(1.25, -st.scale), t1 - t0, 1/(t1-t0), n, n/(t1-t0)/1e6);
t0 = t1;
}
}
int main(int argc, char **argv) {
st.jitter = 0.;
st.rule = "odd";
int c;
while((c = getopt(argc, argv, "s:j:r:")) > 0) {
switch(c) {
case 'r': st.rule = optarg; break;
case 's': st.scale = atof(optarg); break;
case 'j': st.jitter = atof(optarg); break;
default:
usage();
}
}
auto nargs = argc - optind;
if(nargs != 2) usage();
auto patfile = argv[optind];
auto segfile = argv[optind+1];
st.h = HatchPattern::FromFile(patfile, 1.);
st.c = ContoursFromFile(segfile);
Segments s;
ContoursToSegments(s, st.c);
static auto cmp_x = [](const Segment &a, const Segment & b)
{ return a.p.x < b.p.x; };
static auto cmp_y = [](const Segment &a, const Segment & b)
{ return a.p.y < b.p.y; };
float min_x = std::min_element(s.begin(), s.end(), cmp_x)->p.x * 1.1;
float max_x = std::max_element(s.begin(), s.end(), cmp_x)->p.x * 1.1;
float min_y = -std::max_element(s.begin(), s.end(), cmp_y)->p.y * 1.1;
float max_y = -std::min_element(s.begin(), s.end(), cmp_y)->p.y * 1.1;
float d_x = max_x - min_x;
float d_y = max_y - min_y;
// set up view matrix
st.mat =
glm::translate(glm::fvec3{-1.f, -1.f, 0.f}) *
glm::scale(glm::fvec3{2.f/d_x, 2.f/d_y, 1.f}) *
glm::translate(glm::fvec3{-min_x, -min_y, 0.f});
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* window = SDL_CreateWindow("GL Dashing",
0, 0, 1600, 900, SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL);
SDL_GL_CreateContext(window);
setup();
mainloop(window);
return 0;
}

143
glsetup.cc Normal file
View file

@ -0,0 +1,143 @@
#include <GL/glew.h>
#include <cstdio>
#include <cmath>
#include <iostream>
#include "gldashing.hh"
int ifloor(double x) { return int(floor(x)); }
int iceil(double x) { return int(ceil(x)); }
int iround(double x) { return int(round(x)); }
#define SHADER_SOURCE(...) #__VA_ARGS__
void setup()
{
glewInit();
GLint compile_ok = GL_FALSE, link_ok = GL_FALSE;
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
const char *vs_source =
"#version 300 es\n"
"#ifdef GL_FRAGMENT_PRECISION_HIGH\n"
"# define maxfragp highp\n"
"#else\n"
"# define maxfragp medp\n"
"#endif\n"
SHADER_SOURCE(
uniform maxfragp mat4 uXf;
in maxfragp vec4 aPos;
in mediump vec3 aColor;
out vec4 vColor;
out vec2 vTexCoord;
void main(void)
{
gl_Position = uXf * vec4(aPos.xy, 0., 1.);
vColor = vec4(aColor, 1.);
vTexCoord = aPos.zw;
}
);
glShaderSource(vs, 1, &vs_source, NULL);
glCompileShader(vs);
glGetShaderiv(vs, GL_COMPILE_STATUS, &compile_ok);
if (!compile_ok) {
std::cerr << "Error in vertex shader\n";
abort();
}
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
const char *fs_source =
"#version 300 es\n"
SHADER_SOURCE(
in mediump vec4 vColor;
in mediump vec2 vTexCoord;
out mediump vec4 fragColor;
uniform mediump sampler2DArray uTex;
void main(void) {
mediump float coverage =
texture(uTex, vec3(vTexCoord.x, 0, vTexCoord.y)).r;
coverage = (vTexCoord.y < 0.) ? 1. : coverage;
fragColor = vec4(vColor.rgb * coverage, coverage);
}
);
glShaderSource(fs, 1, &fs_source, NULL);
glCompileShader(fs);
glGetShaderiv(fs, GL_COMPILE_STATUS, &compile_ok);
if (!compile_ok) {
std::cerr << "Error in fragment shader\n";
printf("%s\n", fs_source);
abort();
}
GLuint program = glCreateProgram();
glAttachShader(program, vs);
glAttachShader(program, fs);
glLinkProgram(program);
glGetProgramiv(program, GL_LINK_STATUS, &link_ok);
if (!link_ok) {
std::cerr << "Error in glLinkProgram\n";
abort();
}
glUseProgram(program);
glClearColor(1., 1., 1., 1.);
st.attribute_aPos = glGetAttribLocation(program, "aPos");
st.attribute_aColor = glGetAttribLocation(program, "aColor");
st.uniform_uXf = glGetUniformLocation(program, "uXf");
st.uniform_uTex = glGetUniformLocation(program, "uTex");
glEnableVertexAttribArray(st.attribute_aPos);
glUniformMatrix4fv(st.uniform_uXf, 1, false, &st.mat[0][0]);
glGenTextures(1, &st.texture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_ARRAY, st.texture);
glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_R8, 256, 256, st.h.d.size());
glUniform1i(st.uniform_uTex, 0);
for(size_t i=0; i<st.h.d.size(); i++) {
auto &th = st.h.d[i];
auto total = th.sum.back();
float coverage[256];
auto np = 256;
std::fill(coverage, coverage+np, 0.f);
for(size_t j=0; j<th.dash.size(); j++) {
auto di = th.dash[j];
if(di < 0) continue;
auto lo = th.sum[j], lof = lo * np/total;
auto lop = ifloor(lof);
auto loc = (lop + 1) - lof;
if(di == 0) { coverage[lop] += 256. / np; continue; }
auto hi = th.sum[j+1], hif = hi * np/total;
auto hip = iceil(hif);
auto hic = 1 - (hip - hif);
coverage[lop] += loc;
for(int k = lop+1; k < hip-1; k++) coverage[k] = 1.;
coverage[hip-1] += hic;
}
for(auto j=0; j<np; j++) coverage[j] = std::min(1.f, std::max(0.f, coverage[j]));
for(auto j=0; j<np; j++)
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, j, i, np, 1, 1, GL_RED, GL_FLOAT, coverage);
}
glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glEnable(GL_BLEND);
auto i = glGetError();
if(i) { printf("gl error: %d\n", (int)i); abort(); }
}

View file

@ -128,7 +128,6 @@ int main(int argc, char **argv) {
for(const auto i : s) print_seg(i);
std::cout << "\"/>";
std::vector<Segment> segs;
std::cout << "<path fill=\"none\" stroke=\"blue\" stroke-opacity=\".8\" "
"stroke-linecap=\"round\" d=\"";
xyhatch(h, s, print_seg, rule);