Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webgl: FBO support preparation #13309

Merged
merged 5 commits into from Sep 21, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions components/layout/webrender_helpers.rs
Expand Up @@ -427,9 +427,12 @@ impl WebRenderDisplayItemConverter for DisplayItem {
if let Some(id) = item.webrender_image.key {
if item.stretch_size.width > Au(0) &&
item.stretch_size.height > Au(0) {
// TODO(gw): Pass through the tile spacing once the other
// changes related to this land (parsing etc).
builder.push_image(item.base.bounds.to_rectf(),
item.base.clip.to_clip_region(frame_builder),
item.stretch_size.to_sizef(),
Size2D::zero(),
item.image_rendering.to_image_rendering(),
id);
}
Expand Down
7 changes: 7 additions & 0 deletions components/script/dom/webglframebuffer.rs
Expand Up @@ -5,6 +5,7 @@
// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use canvas_traits::CanvasMsg;
use dom::bindings::codegen::Bindings::WebGLFramebufferBinding;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::Root;
use dom::bindings::reflector::reflect_dom_object;
Expand Down Expand Up @@ -79,6 +80,12 @@ impl WebGLFramebuffer {
self.is_deleted.get()
}

pub fn check_status(&self) -> u32 {
// Until we build support for attaching renderbuffers or
// textures, all user FBOs are incomplete.
return constants::FRAMEBUFFER_UNSUPPORTED;
}

pub fn target(&self) -> Option<u32> {
self.target.get()
}
Expand Down
62 changes: 61 additions & 1 deletion components/script/dom/webglrenderingcontext.rs
Expand Up @@ -212,6 +212,38 @@ impl WebGLRenderingContext {
}
}

// Helper function for validating framebuffer completeness in
// calls touching the framebuffer. From the GLES 2.0.25 spec,
// page 119:
//
// "Effects of Framebuffer Completeness on Framebuffer
// Operations
//
// If the currently bound framebuffer is not framebuffer
// complete, then it is an error to attempt to use the
// framebuffer for writing or reading. This means that
// rendering commands such as DrawArrays and DrawElements, as
// well as commands that read the framebuffer such as
// ReadPixels and CopyTexSubImage, will generate the error
// INVALID_FRAMEBUFFER_OPERATION if called while the
// framebuffer is not framebuffer complete."
//
// The WebGL spec mentions a couple more operations that trigger
// this: clear() and getParameter(IMPLEMENTATION_COLOR_READ_*).
fn validate_framebuffer_complete(&self) -> bool {
match self.bound_framebuffer.get() {
Some(fb) => match fb.check_status() {
constants::FRAMEBUFFER_COMPLETE => return true,
_ => {
self.webgl_error(InvalidFramebufferOperation);
return false;
}
},
// The default framebuffer is always complete.
None => return true,
}
}

fn tex_parameter(&self, target: u32, name: u32, value: TexParameterValue) {
let texture = match target {
constants::TEXTURE_2D => self.bound_texture_2d.get(),
Expand Down Expand Up @@ -591,6 +623,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
let error_code = if let Some(error) = self.last_error.get() {
match error {
WebGLError::InvalidEnum => constants::INVALID_ENUM,
WebGLError::InvalidFramebufferOperation => constants::INVALID_FRAMEBUFFER_OPERATION,
WebGLError::InvalidValue => constants::INVALID_VALUE,
WebGLError::InvalidOperation => constants::INVALID_OPERATION,
WebGLError::OutOfMemory => constants::OUT_OF_MEMORY,
Expand Down Expand Up @@ -748,9 +781,11 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
// case: Chromium currently unbinds, and Gecko silently
// returns. The conformance tests don't cover this case.
Some(renderbuffer) if !renderbuffer.is_deleted() => {
renderbuffer.bind(target)
self.bound_renderbuffer.set(Some(renderbuffer));
renderbuffer.bind(target);
}
_ => {
self.bound_renderbuffer.set(None);
// Unbind the currently bound renderbuffer
self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::BindRenderbuffer(target, None)))
Expand All @@ -773,6 +808,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
Err(err) => return self.webgl_error(err),
}
} else {
slot.set(None);
// Unbind the currently bound texture
self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::BindTexture(target, None)))
Expand Down Expand Up @@ -882,6 +918,10 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
fn CopyTexImage2D(&self, target: u32, level: i32, internal_format: u32,
x: i32, y: i32, width: i32, height: i32, border: i32) {
if !self.validate_framebuffer_complete() {
return;
}

let validator = CommonTexImage2DValidator::new(self, target, level,
internal_format, width,
height, border);
Expand Down Expand Up @@ -935,6 +975,10 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
fn CopyTexSubImage2D(&self, target: u32, level: i32, xoffset: i32, yoffset: i32,
x: i32, y: i32, width: i32, height: i32) {
if !self.validate_framebuffer_complete() {
return;
}

// NB: We use a dummy (valid) format and border in order to reuse the
// common validations, but this should have its own validator.
let validator = CommonTexImage2DValidator::new(self, target, level,
Expand Down Expand Up @@ -974,6 +1018,10 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {

// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11
fn Clear(&self, mask: u32) {
if !self.validate_framebuffer_complete() {
return;
}

self.ipc_renderer.send(CanvasMsg::WebGL(WebGLCommand::Clear(mask))).unwrap();
self.mark_as_dirty();
}
Expand Down Expand Up @@ -1200,6 +1248,10 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
return self.webgl_error(InvalidValue);
}

if !self.validate_framebuffer_complete() {
return;
}

self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::DrawArrays(mode, first, count)))
.unwrap();
Expand Down Expand Up @@ -1236,6 +1288,10 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
return self.webgl_error(InvalidOperation);
}

if !self.validate_framebuffer_complete() {
return;
}

match mode {
constants::POINTS | constants::LINE_STRIP |
constants::LINE_LOOP | constants::LINES |
Expand Down Expand Up @@ -1504,6 +1560,10 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
None => return self.webgl_error(InvalidValue),
};

if !self.validate_framebuffer_complete() {
return;
}

match unsafe { JS_GetArrayBufferViewType(pixels) } {
Type::Uint8 => (),
_ => return self.webgl_error(InvalidOperation)
Expand Down
4 changes: 2 additions & 2 deletions components/servo/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions ports/cef/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 15 additions & 9 deletions resources/shaders/prim_shared.glsl
Expand Up @@ -375,38 +375,44 @@ PrimitiveInfo fetch_text_run_glyph(int index, out vec4 color, out vec4 uv_rect)

struct Image {
PrimitiveInfo info;
vec4 st_rect; // Location of the image texture in the texture atlas.
vec4 stretch_size_uvkind; // Size of the actual image.
vec4 st_rect; // Location of the image texture in the texture atlas.
vec4 stretch_size_and_tile_spacing; // Size of the actual image and amount of space between
// tiled instances of this image.
vec4 uvkind; // Type of texture coordinates.
};

Image fetch_image(int index) {
Image image;

int offset = index * 5;
int offset = index * 6;

image.info = unpack_prim_info(offset);
image.st_rect = data[offset + 3];
image.stretch_size_uvkind = data[offset + 4];
image.stretch_size_and_tile_spacing = data[offset + 4];
image.uvkind = data[offset + 5];

return image;
}

struct ImageClip {
PrimitiveInfo info;
vec4 st_rect; // Location of the image texture in the texture atlas.
vec4 stretch_size_uvkind; // Size of the actual image.
vec4 st_rect; // Location of the image texture in the texture atlas.
vec4 stretch_size_and_tile_spacing; // Size of the actual image and amount of space between
// tiled instances of this image.
vec4 uvkind; // Type of texture coordinates.
Clip clip;
};

ImageClip fetch_image_clip(int index) {
ImageClip image;

int offset = index * 14;
int offset = index * 15;

image.info = unpack_prim_info(offset);
image.st_rect = data[offset + 3];
image.stretch_size_uvkind = data[offset + 4];
image.clip = unpack_clip(offset + 5);
image.stretch_size_and_tile_spacing = data[offset + 4];
image.uvkind = data[offset + 5];
image.clip = unpack_clip(offset + 6);

return image;
}
Expand Down
21 changes: 12 additions & 9 deletions resources/shaders/ps_image.fs.glsl
Expand Up @@ -11,16 +11,19 @@ void main(void) {

// We clamp the texture coordinate calculation here to the local rectangle boundaries,
// which makes the edge of the texture stretch instead of repeat.
vec2 uv = clamp(pos, vLocalRect.xy, vLocalRect.xy + vLocalRect.zw);

uv = (uv - vLocalRect.xy) / vStretchSize;
vec2 relative_pos_in_rect =
clamp(pos, vLocalRect.xy, vLocalRect.xy + vLocalRect.zw) - vLocalRect.xy;
#else
vec2 uv = vUv;
float alpha = 1.0;;
vec2 relative_pos_in_rect = vLocalPos;
#endif
vec2 st = vTextureOffset + vTextureSize * fract(uv);
#ifdef WR_FEATURE_TRANSFORM

// We calculate the particular tile this fragment belongs to, taking into
// account the spacing in between tiles. We only paint if our fragment does
// not fall into that spacing.
vec2 position_in_tile = mod(relative_pos_in_rect, vStretchSize + vTileSpacing);
vec2 st = vTextureOffset + ((position_in_tile / vStretchSize) * vTextureSize);
alpha = alpha * float(all(bvec2(step(position_in_tile, vStretchSize))));

oFragColor = vec4(1, 1, 1, alpha) * texture(sDiffuse, st);
#else
oFragColor = texture(sDiffuse, st);
#endif
}
4 changes: 3 additions & 1 deletion resources/shaders/ps_image.glsl
Expand Up @@ -4,11 +4,13 @@

flat varying vec2 vTextureOffset; // Offset of this image into the texture atlas.
flat varying vec2 vTextureSize; // Size of the image in the texture atlas.
flat varying vec2 vTileSpacing; // Amount of space between tiled instances of this image.

#ifdef WR_FEATURE_TRANSFORM
varying vec3 vLocalPos;
flat varying vec4 vLocalRect;
flat varying vec2 vStretchSize;
#else
varying vec2 vUv; // Location within the CSS box to draw.
varying vec2 vLocalPos;
flat varying vec2 vStretchSize;
#endif
8 changes: 5 additions & 3 deletions resources/shaders/ps_image.vs.glsl
Expand Up @@ -10,17 +10,18 @@ void main(void) {
TransformVertexInfo vi = write_transform_vertex(image.info);
vLocalRect = vi.clipped_local_rect;
vLocalPos = vi.local_pos;
vStretchSize = image.stretch_size_uvkind.xy;
vStretchSize = image.stretch_size_and_tile_spacing.xy;
#else
VertexInfo vi = write_vertex(image.info);
vUv = (vi.local_clamped_pos - vi.local_rect.p0) / image.stretch_size_uvkind.xy;
vStretchSize = image.stretch_size_and_tile_spacing.xy;
vLocalPos = vi.local_clamped_pos - vi.local_rect.p0;
#endif

// vUv will contain how many times this image has wrapped around the image size.
vec2 st0 = image.st_rect.xy;
vec2 st1 = image.st_rect.zw;

switch (uint(image.stretch_size_uvkind.z)) {
switch (uint(image.uvkind.x)) {
case UV_NORMALIZED:
break;
case UV_PIXEL: {
Expand All @@ -33,4 +34,5 @@ void main(void) {

vTextureSize = st1 - st0;
vTextureOffset = st0;
vTileSpacing = image.stretch_size_and_tile_spacing.zw;
}
17 changes: 11 additions & 6 deletions resources/shaders/ps_image_clip.fs.glsl
Expand Up @@ -11,17 +11,22 @@ void main(void) {

// We clamp the texture coordinate calculation here to the local rectangle boundaries,
// which makes the edge of the texture stretch instead of repeat.
vec2 uv = clamp(local_pos.xy, vLocalRect.xy, vLocalRect.xy + vLocalRect.zw);

uv = (uv - vLocalRect.xy) / vStretchSize;
vec2 pos_for_texture =
clamp(pos, vLocalRect.xy, vLocalRect.xy + vLocalRect.zw) - vLocalRect.xy;
#else
float alpha = 1;
vec2 local_pos = vLocalPos;
vec2 uv = vUv;
vec2 relative_pos_in_rect = vLocalPos - vLocalRect.xy;
#endif

vec2 st = vTextureOffset + vTextureSize * fract(uv);

alpha = min(alpha, do_clip(local_pos, vClipRect, vClipRadius));

// We calculate the particular tile this fragment belongs to, taking into
// account the spacing in between tiles. We only paint if our fragment does
// not fall into that spacing.
vec2 position_in_tile = mod(relative_pos_in_rect, vStretchSize + vTileSpacing);
vec2 st = vTextureOffset + ((position_in_tile / vStretchSize) * vTextureSize);
alpha = alpha * float(all(bvec2(step(position_in_tile, vStretchSize))));

oFragColor = texture(sDiffuse, st) * vec4(1, 1, 1, alpha);
}
5 changes: 3 additions & 2 deletions resources/shaders/ps_image_clip.glsl
Expand Up @@ -4,13 +4,14 @@

flat varying vec2 vTextureOffset; // Offset of this image into the texture atlas.
flat varying vec2 vTextureSize; // Size of the image in the texture atlas.
flat varying vec2 vTileSpacing; // Amount of space between tiled instances of this image.
flat varying vec2 vStretchSize;
flat varying vec4 vClipRect;
flat varying vec4 vClipRadius;
flat varying vec4 vLocalRect;

#ifdef WR_FEATURE_TRANSFORM
varying vec3 vLocalPos;
flat varying vec4 vLocalRect;
flat varying vec2 vStretchSize;
#else
varying vec2 vLocalPos;
varying vec2 vUv; // Location within the CSS box to draw.
Expand Down