first shot at composited blending

still missing:
- stencil+depth mask
- alpha compositing
This commit is contained in:
Konstantin Käfer
2014-05-20 17:38:52 +02:00
parent 238fe86571
commit 13939b26d9
23 changed files with 325 additions and 23 deletions

View File

@@ -40,7 +40,10 @@ public:
}
glBindBuffer(bufferType, buffer);
if (force) {
assert("Buffer was already deleted" && array != nullptr);
if (array == nullptr) {
throw std::runtime_error("Buffer was already deleted or doesn't contain elements");
}
glBufferData(bufferType, pos, array, GL_STATIC_DRAW);
if (!retainAfterUpload) {
cleanup();
@@ -58,11 +61,15 @@ public:
protected:
// increase the buffer size by at least /required/ bytes.
inline void *addElement() {
assert("Buffer is already bound to GPU" && buffer == 0);
if (buffer != 0) {
throw std::runtime_error("Can't add elements after buffer was bound to GPU");
}
if (length < pos + itemSize) {
while (length < pos + itemSize) length += defaultLength;
array = realloc(array, length);
assert("Buffer reallocation failed" && array != nullptr);
if (array == nullptr) {
throw std::runtime_error("Buffer reallocation failed¯");
}
}
pos += itemSize;
return static_cast<char *>(array) + (pos - itemSize);
@@ -70,7 +77,9 @@ protected:
// Get a pointer to the item at a given index.
inline void *getElement(size_t index) {
assert("Buffer was deleted" && array != nullptr);
if (array == nullptr) {
throw std::runtime_error("Buffer was already deleted or doesn't contain elements");
}
if (index * itemSize >= pos) {
throw new std::runtime_error("Can't get element after array bounds");

View File

@@ -123,6 +123,11 @@ private:
void renderLayers(const std::vector<LayerDescription>& layers);
void renderLayer(const LayerDescription& layer_desc, RenderPass pass);
void clearFramebuffers();
void bindFramebuffer();
void pushFramebuffer();
GLuint popFramebuffer();
private:
// If cleared, the next time the render thread attempts to render the map, it will *actually*
// render the map.
@@ -154,6 +159,10 @@ private:
int indent = 0;
std::vector<GLuint> fbos;
std::vector<GLuint> fbos_color;
int fbo_level = -1;
private:
bool async = false;
uv_loop_t *loop = nullptr;

View File

@@ -18,6 +18,7 @@
#include <llmr/shader/raster_shader.hpp>
#include <llmr/shader/text_shader.hpp>
#include <llmr/shader/dot_shader.hpp>
#include <llmr/shader/composite_shader.hpp>
#include <llmr/map/transform_state.hpp>
@@ -86,6 +87,8 @@ public:
void drawClippingMask(const mat4& matrix, const ClipID& clip);
void finishClippingMask();
void drawComposite(GLuint texture, const CompositeProperties &properties);
bool needsAnimation() const;
private:
void setupShaders();
@@ -127,6 +130,7 @@ private:
std::unique_ptr<RasterShader> rasterShader;
std::unique_ptr<TextShader> textShader;
std::unique_ptr<DotShader> dotShader;
std::unique_ptr<CompositeShader> compositeShader;
// Set up the stencil quad we're using to generate the stencil mask.
VertexBuffer tileStencilBuffer = {
@@ -144,6 +148,8 @@ private:
VertexArrayObject coveringPlainArray;
VertexArrayObject coveringPatternArray;
VertexArrayObject coveringRasterArray;
VertexArrayObject compositeArray;
VertexArrayObject matteArray;
// Set up the tile boundary lines we're using to draw the tile outlines.

View File

@@ -0,0 +1,29 @@
#ifndef LLMR_SHADER_COMPOSITE_SHADER
#define LLMR_SHADER_COMPOSITE_SHADER
#include <llmr/shader/shader.hpp>
namespace llmr {
class CompositeShader : public Shader {
public:
CompositeShader();
void bind(char *offset);
void setImage(int32_t image);
void setOpacity(float opacity);
private:
int32_t a_pos = -1;
int32_t image = 0;
int32_t u_image = -1;
float opacity = 0;
int32_t u_opacity = -1;
};
}
#endif

View File

@@ -17,10 +17,11 @@ public:
private:
int32_t a_pos = -1;
int32_t image = -1;
int32_t image = 0;
int32_t u_image = -1;
float opacity = 0.0f;
float u_opacity = 0.0f;
float opacity = 0;
int32_t u_opacity = -1;
};
}

View File

@@ -16,6 +16,7 @@ public:
std::map<std::string, IconClass> icon;
std::map<std::string, TextClass> text;
std::map<std::string, RasterClass> raster;
std::map<std::string, CompositeClass> composite;
};

View File

@@ -180,6 +180,16 @@ struct RasterProperties {
float opacity = 1.0;
};
struct CompositeClass {
FunctionProperty enabled = true;
FunctionProperty opacity = 1;
};
struct CompositeProperties {
bool enabled = true;
float opacity = 1.0;
};
}
#endif

View File

@@ -50,6 +50,7 @@ public:
std::map<std::string, IconProperties> icons;
std::map<std::string, TextProperties> texts;
std::map<std::string, RasterProperties> rasters;
std::map<std::string, CompositeProperties> composites;
} computed;
};

View File

@@ -36,6 +36,7 @@ private:
TextClass parseTextClass(JSVal value);
BackgroundClass parseBackgroundClass(JSVal value);
RasterClass parseRasterClass(JSVal value);
CompositeClass parseCompositeClass(JSVal value);
bool parseBoolean(JSVal value);
std::string parseString(JSVal value);

View File

@@ -14,9 +14,9 @@ extern const float tileSize;
namespace debug {
const bool styleParseWarnings = true;
const bool renderWarnings = true;
const bool renderTree = true;
extern const bool styleParseWarnings;
extern const bool renderWarnings;
extern const bool renderTree;
}

View File

@@ -35,6 +35,8 @@ Map::Map(View& view)
}
Map::~Map() {
clearFramebuffers();
uv_loop_delete(loop);
loop = nullptr;
}
@@ -513,38 +515,59 @@ void Map::renderLayers(const std::vector<LayerDescription>& layers) {
}
template <typename Styles>
bool is_invisible(const Styles &styles, const LayerDescription &layer_desc) {
typename Styles::const_iterator find_style(const Styles &styles, const LayerDescription &layer_desc) {
auto it = styles.find(layer_desc.name);
if (it == styles.end()) {
if (debug::renderWarnings) {
fprintf(stderr, "[WARNING] can't find class named '%s' in style\n",
layer_desc.name.c_str());
}
return true;
}
const auto &properties = it->second;
return it;
}
template <typename Properties>
bool is_invisible(const Properties &properties) {
if (!properties.enabled) { return true; }
if (properties.opacity <= 0) { return true; }
return false;
}
template <typename Styles>
bool is_invisible(const Styles &styles, const LayerDescription &layer_desc) {
auto it = find_style(styles, layer_desc);
if (it == styles.end()) { return true; }
return is_invisible(it->second);
}
void Map::renderLayer(const LayerDescription& layer_desc, RenderPass pass) {
if (layer_desc.child_layer.size()) {
// This is a layer group. We render them during our translucent render pass.
if (pass == Translucent) {
gl::group group(std::string("group: ") + layer_desc.name);
auto it = find_style(style.computed.composites, layer_desc);
if (it != style.computed.composites.end() && !is_invisible(it->second)) {
const CompositeProperties &properties = it->second;
gl::group group(std::string("group: ") + layer_desc.name);
if (debug::renderTree) {
std::cout << std::string(indent++ * 4, ' ') << "+ " << layer_desc.name
<< " (Composite) {" << std::endl;
}
if (debug::renderTree) {
std::cout << std::string(indent++ * 4, ' ') << "+ " << layer_desc.name
<< " (Composite) {" << std::endl;
}
// TODO: create framebuffer
renderLayers(layer_desc.child_layer);
// TODO: render framebuffer on previous framebuffer
pushFramebuffer();
if (debug::renderTree) {
std::cout << std::string(--indent * 4, ' ') << "}" << std::endl;
glClear(GL_COLOR_BUFFER_BIT);
renderLayers(layer_desc.child_layer);
GLuint texture = popFramebuffer();
// Render the previous texture onto the screen.
painter.drawComposite(texture, properties);
if (debug::renderTree) {
std::cout << std::string(--indent * 4, ' ') << "}" << std::endl;
}
}
}
} else if (layer_desc.bucket_name == "background") {
@@ -607,3 +630,73 @@ void Map::renderLayer(const LayerDescription& layer_desc, RenderPass pass) {
}
}
}
void Map::clearFramebuffers() {
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
// Delete any framebuffers that we might have allocated
glDeleteTextures(fbos_color.size(), fbos.data());
fbos_color.clear();
glDeleteFramebuffersEXT(fbos.size(), fbos.data());
fbos.clear();
}
void Map::bindFramebuffer() {
if (fbo_level < 0) {
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
} else if (fbos.size() > (size_t)fbo_level) {
GLuint fbo = fbos[fbo_level];
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
} else {
// TODO: Convert this to textures so we can composite them and use them in blend operations
GLuint fbo_color;
glGenTextures(1, &fbo_color);
glBindTexture(GL_TEXTURE_2D, fbo_color);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, state.getFramebufferWidth(), state.getFramebufferHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glBindTexture(GL_TEXTURE_2D, 0);
fbos_color.emplace_back(fbo_color);
// TODO: Stencil/Depth buffer
// Allocate new framebuffer
GLuint fbo = 0;
glGenFramebuffersEXT(1, &fbo);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
fbos.emplace_back(fbo);
// Assemble the FBO
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo_color, 0);
// TODO: Stencil/Depth buffer
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
fprintf(stderr, "Couldn't create framebuffer: ");
switch (status) {
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: fprintf(stderr, "incomplete attachment\n"); break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: fprintf(stderr, "incomplete missing attachment\n"); break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: fprintf(stderr, "incomplete draw buffer\n"); break;
case GL_FRAMEBUFFER_UNSUPPORTED: fprintf(stderr, "unsupported\n"); break;
default: fprintf(stderr, "other\n"); break;
}
return;
}
}
}
void Map::pushFramebuffer() {
fbo_level++;
bindFramebuffer();
}
GLuint Map::popFramebuffer() {
GLuint texture = fbos_color[fbo_level];
fbo_level--;
bindFramebuffer();
return texture;
}

View File

@@ -360,6 +360,9 @@ void LineBucket::drawLines(LineShader& shader) {
char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
char *elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize);
for (triangle_group_type& group : triangleGroups) {
if (!group.elements_length) {
continue;
}
group.array.bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index);
glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index);
vertex_index += group.vertex_length * vertexBuffer.itemSize;
@@ -371,6 +374,9 @@ void LineBucket::drawPoints(LinejoinShader& shader) {
char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
char *elements_index = BUFFER_OFFSET(point_elements_start * pointElementsBuffer.itemSize);
for (point_group_type& group : pointGroups) {
if (!group.elements_length) {
continue;
}
group.array.bind(shader, vertexBuffer, pointElementsBuffer, vertex_index);
glDrawElements(GL_POINTS, group.elements_length, GL_UNSIGNED_SHORT, elements_index);
vertex_index += group.vertex_length * vertexBuffer.itemSize;

View File

@@ -38,6 +38,7 @@ void Painter::setup() {
assert(rasterShader);
assert(textShader);
assert(dotShader);
assert(compositeShader);
// Blending
@@ -67,6 +68,7 @@ void Painter::setupShaders() {
rasterShader = std::make_unique<RasterShader>();
textShader = std::make_unique<TextShader>();
dotShader = std::make_unique<DotShader>();
compositeShader = std::make_unique<CompositeShader>();
}
void Painter::resize() {

View File

@@ -0,0 +1,25 @@
#include <llmr/renderer/painter.hpp>
#include <iostream>
using namespace llmr;
void Painter::drawComposite(GLuint texture, const CompositeProperties &properties) {
glDisable(GL_STENCIL_TEST);
useProgram(compositeShader->program);
compositeShader->setMatrix(nativeMatrix);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
compositeShader->setImage(0);
// TODO: layerstyle
compositeShader->setOpacity(properties.opacity);
// Draw the clipping mask
compositeArray.bind(*compositeShader, tileStencilBuffer, BUFFER_OFFSET(0));
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)tileStencilBuffer.index());
glEnable(GL_STENCIL_TEST);
}

View File

@@ -0,0 +1,8 @@
uniform sampler2D u_image;
uniform float u_opacity;
varying vec2 v_pos;
void main() {
gl_FragColor = texture2D(u_image, v_pos / 2.0 + 0.5) * u_opacity;
}

View File

@@ -0,0 +1,9 @@
attribute vec2 a_pos;
uniform mat4 u_matrix;
varying vec2 v_pos;
void main() {
gl_Position = u_matrix * vec4(a_pos, 0, 1);
v_pos = gl_Position.xy;
}

View File

@@ -0,0 +1,48 @@
#include <llmr/shader/composite_shader.hpp>
#include <llmr/shader/shaders.hpp>
#include <llmr/platform/gl.hpp>
#include <cstdio>
using namespace llmr;
CompositeShader::CompositeShader()
: Shader(
shaders[COMPOSITE_SHADER].vertex,
shaders[COMPOSITE_SHADER].fragment
) {
if (!valid) {
fprintf(stderr, "invalid composite shader\n");
return;
}
a_pos = glGetAttribLocation(program, "a_pos");
u_matrix = glGetUniformLocation(program, "u_matrix");
u_image = glGetUniformLocation(program, "u_image");
u_opacity = glGetUniformLocation(program, "u_opacity");
// fprintf(stderr, "CompositeShader:\n");
// fprintf(stderr, " - u_matrix: %d\n", u_matrix);
// fprintf(stderr, " - u_image: %d\n", u_image);
// fprintf(stderr, " - u_opacity: %f\n", u_opacity);
}
void CompositeShader::bind(char *offset) {
glEnableVertexAttribArray(a_pos);
glVertexAttribPointer(a_pos, 2, GL_SHORT, false, 0, offset);
}
void CompositeShader::setImage(int32_t new_image) {
if (image != new_image) {
glUniform1i(u_image, new_image);
image = new_image;
}
}
void CompositeShader::setOpacity(float new_opacity) {
if (opacity != new_opacity) {
glUniform1f(u_opacity, new_opacity);
opacity = new_opacity;
}
}

View File

@@ -19,6 +19,7 @@ RasterShader::RasterShader()
a_pos = glGetAttribLocation(program, "a_pos");
u_matrix = glGetUniformLocation(program, "u_matrix");
u_image = glGetUniformLocation(program, "u_image");
u_opacity = glGetUniformLocation(program, "u_opacity");
// fprintf(stderr, "RasterShader:\n");

View File

@@ -6,6 +6,10 @@
using namespace llmr;
const shader_source llmr::shaders[SHADER_COUNT] = {
{
"#version 120\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nvarying vec2 v_pos;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n vec4 tmpvar_2;\n tmpvar_2 = (u_matrix * tmpvar_1);\n gl_Position = tmpvar_2;\n v_pos = tmpvar_2.xy;\n}\n\n",
"#version 120\nuniform sampler2D u_image;\nuniform float u_opacity;\nvarying vec2 v_pos;\nvoid main ()\n{\n gl_FragColor = (texture2D (u_image, ((v_pos / 2.0) + 0.5)) * u_opacity);\n}\n\n",
},
{
"#version 120\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nuniform float u_size;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n gl_Position = (u_matrix * tmpvar_1);\n gl_PointSize = u_size;\n}\n\n",
"#version 120\nuniform vec4 u_color;\nuniform float u_blur;\nvoid main ()\n{\n vec2 x_1;\n x_1 = (gl_PointCoord - 0.5);\n float tmpvar_2;\n tmpvar_2 = clamp (((\n sqrt(dot (x_1, x_1))\n - 0.5) / (\n (0.5 - u_blur)\n - 0.5)), 0.0, 1.0);\n gl_FragColor = (u_color * (tmpvar_2 * (tmpvar_2 * \n (3.0 - (2.0 * tmpvar_2))\n )));\n}\n\n",

View File

@@ -6,6 +6,10 @@
using namespace llmr;
const shader_source llmr::shaders[SHADER_COUNT] = {
{
"precision highp float;\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nvarying highp vec2 v_pos;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n vec4 tmpvar_2;\n tmpvar_2 = (u_matrix * tmpvar_1);\n gl_Position = tmpvar_2;\n v_pos = tmpvar_2.xy;\n}\n\n",
"precision highp float;\nuniform sampler2D u_image;\nuniform float u_opacity;\nvarying vec2 v_pos;\nvoid main ()\n{\n lowp vec4 tmpvar_1;\n tmpvar_1 = (texture2D (u_image, ((v_pos / 2.0) + 0.5)) * u_opacity);\n gl_FragColor = tmpvar_1;\n}\n\n",
},
{
"precision highp float;\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nuniform float u_size;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n gl_Position = (u_matrix * tmpvar_1);\n gl_PointSize = u_size;\n}\n\n",
"precision highp float;\nuniform vec4 u_color;\nuniform float u_blur;\nvoid main ()\n{\n mediump vec2 x_1;\n x_1 = (gl_PointCoord - 0.5);\n mediump float tmpvar_2;\n tmpvar_2 = clamp (((\n sqrt(dot (x_1, x_1))\n - 0.5) / (\n (0.5 - u_blur)\n - 0.5)), 0.0, 1.0);\n gl_FragColor = (u_color * (tmpvar_2 * (tmpvar_2 * \n (3.0 - (2.0 * tmpvar_2))\n )));\n}\n\n",

View File

@@ -18,6 +18,7 @@ void Style::reset() {
computed.icons.clear();
computed.texts.clear();
computed.rasters.clear();
computed.composites.clear();
}
void Style::cascade(float z) {
@@ -130,6 +131,19 @@ void Style::cascade(float z) {
raster.opacity = layer.opacity.evaluate<float>(z);
}
// Cascade composite classes
for (const auto& composite_pair : sheetClass.composite) {
const std::string& layer_name = composite_pair.first;
const llmr::CompositeClass& layer = composite_pair.second;
// TODO: This should be restricted to composite styles that have actual
// values so as to not override with default values.
llmr::CompositeProperties& composite = computed.composites[layer_name];
composite.enabled = layer.enabled.evaluate<bool>(z);
composite.opacity = layer.opacity.evaluate<float>(z);
}
// Cascade background
computed.background.color = sheetClass.background.color;
computed.background.opacity = sheetClass.background.opacity.evaluate<float>(z);

View File

@@ -297,6 +297,9 @@ void StyleParser::parseClass(const std::string &name, JSVal value, ClassDescript
if (bucket_name == "background") {
// background buckets are fake
class_desc.background = parseBackgroundClass(value);
} else if (bucket_name.length() == 0) {
// no bucket name == composite bucket.
class_desc.composite.insert({ name, std::forward<CompositeClass>(parseCompositeClass(value)) });
} else {
auto bucket_it = buckets.find(bucket_name);
if (bucket_it == buckets.end()) {
@@ -655,6 +658,20 @@ RasterClass StyleParser::parseRasterClass(JSVal value) {
return klass;
}
CompositeClass StyleParser::parseCompositeClass(JSVal value) {
CompositeClass klass;
if (value.HasMember("enabled")) {
klass.enabled = parseFunction(value["enabled"]);
}
if (value.HasMember("opacity")) {
klass.opacity = parseFunction(value["opacity"]);
}
return klass;
}
BackgroundClass StyleParser::parseBackgroundClass(JSVal value) {
BackgroundClass klass;

View File

@@ -1,3 +1,7 @@
#include <llmr/util/constants.hpp>
const float llmr::util::tileSize = 512.0f;
const bool llmr::debug::styleParseWarnings = true;
const bool llmr::debug::renderWarnings = true;
const bool llmr::debug::renderTree = false;