Commit 271d12ac authored by Lee Salzman's avatar Lee Salzman
Browse files

Bug 1689245 - implement GL_ARB_clear_texture extension for SWGL. r=mattwoodrow

GL_ARB_clear_texture implements some useful GL entry-points for clears
that can clear arbitrary sub-rects of the screen without having to latch
or disturb GL state. This refactors the glClear implementation to be in
terms of the provided glClearTexSubImage extension which is far more
flexible.

This further allows us to reuse clearing primitives in RenderCompositorSWGL
without having to use more expensive DrawTarget versions.

Differential Revision: https://phabricator.services.mozilla.com/D103252
parent 99a6a0db
Loading
Loading
Loading
Loading
+3 −6
Original line number Diff line number Diff line
@@ -124,15 +124,12 @@ bool RenderCompositorSWGL::AllocateMappedBuffer(
                                      rect.size.width, rect.size.height));
  }

  RefPtr<DrawTarget> dt = gfx::Factory::CreateDrawTargetForData(
      BackendType::SKIA, mMappedData, bounds.Size().ToUnknownSize(),
      mMappedStride, SurfaceFormat::B8G8R8A8, false);

  LayoutDeviceIntRegion clear;
  clear.Sub(mRegion, opaque);
  for (auto iter = clear.RectIter(); !iter.Done(); iter.Next()) {
    dt->ClearRect(
        IntRectToRect((iter.Get() - bounds.TopLeft()).ToUnknownRect()));
    const auto& rect = iter.Get();
    wr_swgl_clear_color_rect(mContext, 0, rect.x, rect.y, rect.width,
                             rect.height, 0, 0, 0, 0);
  }

  return true;
+18 −6
Original line number Diff line number Diff line
@@ -91,6 +91,22 @@ pub extern "C" fn wr_swgl_set_texture_buffer(
    );
}

#[no_mangle]
pub extern "C" fn wr_swgl_clear_color_rect(
    ctx: *mut c_void,
    fbo: u32,
    x: i32,
    y: i32,
    width: i32,
    height: i32,
    r: f32,
    g: f32,
    b: f32,
    a: f32,
) {
    swgl::Context::from(ctx).clear_color_rect(fbo, x, y, width, height, r, g, b, a);
}

/// Descriptor for a locked surface that will be directly composited by SWGL.
#[repr(C)]
struct WrSWGLCompositeSurfaceInfo {
@@ -1620,11 +1636,7 @@ impl Compositor for SwCompositor {
    /// frame will not have overlap dependencies assigned and so must instead
    /// be added to the late_surfaces queue to be processed at the end of the
    /// frame.
    fn start_compositing(
        &mut self,
        dirty_rects: &[DeviceIntRect],
        _opaque_rects: &[DeviceIntRect],
    ) {
    fn start_compositing(&mut self, dirty_rects: &[DeviceIntRect], _opaque_rects: &[DeviceIntRect]) {
        // Opaque rects are currently only computed here, not by WR itself, so we
        // ignore the passed parameter and forward our own version onto the native
        // compositor.
+196 −52
Original line number Diff line number Diff line
@@ -600,7 +600,7 @@ struct Texture {
  uint32_t* cleared_rows = nullptr;

  void init_depth_runs(uint16_t z);
  void fill_depth_runs(uint16_t z);
  void fill_depth_runs(uint16_t z, const IntRect& scissor);

  void enable_delayed_clear(uint32_t val) {
    delay_clear = height;
@@ -904,7 +904,7 @@ struct Context {
  bool scissortest = false;
  IntRect scissor = {0, 0, 0, 0};

  uint32_t clearcolor = 0;
  GLfloat clearcolor[4] = {0, 0, 0, 0};
  GLdouble cleardepth = 1;

  int unpack_row_length = 0;
@@ -1290,10 +1290,15 @@ void Disable(GLenum cap) {
GLenum GetError() { return GL_NO_ERROR; }

static const char* const extensions[] = {
    "GL_ARB_blend_func_extended", "GL_ARB_copy_image",
    "GL_ARB_draw_instanced",      "GL_ARB_explicit_attrib_location",
    "GL_ARB_instanced_arrays",    "GL_ARB_invalidate_subdata",
    "GL_ARB_texture_storage",     "GL_EXT_timer_query",
    "GL_ARB_blend_func_extended",
    "GL_ARB_clear_texture",
    "GL_ARB_copy_image",
    "GL_ARB_draw_instanced",
    "GL_ARB_explicit_attrib_location",
    "GL_ARB_instanced_arrays",
    "GL_ARB_invalidate_subdata",
    "GL_ARB_texture_storage",
    "GL_EXT_timer_query",
    "GL_APPLE_rgb_422",
};

@@ -1484,8 +1489,10 @@ void SetScissor(GLint x, GLint y, GLsizei width, GLsizei height) {
}

void ClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
  I32 c = round_pixel((Float){b, g, r, a});
  ctx->clearcolor = bit_cast<uint32_t>(CONVERT(c, U8));
  ctx->clearcolor[0] = r;
  ctx->clearcolor[1] = g;
  ctx->clearcolor[2] = b;
  ctx->clearcolor[3] = a;
}

void ClearDepth(GLdouble depth) { ctx->cleardepth = depth; }
@@ -2371,14 +2378,6 @@ static void clear_buffer(Texture& t, T value, int layer, IntRect bb,
  }
}

template <typename T>
static inline void clear_buffer(Texture& t, T value, int layer = 0) {
  IntRect bb = ctx->apply_scissor(t);
  if (bb.width() > 0) {
    clear_buffer<T>(t, value, layer, bb);
  }
}

template <typename T>
static inline void force_clear_row(Texture& t, int y, int skip_start = 0,
                                   int skip_end = 0) {
@@ -2466,30 +2465,35 @@ static void prepare_texture(Texture& t, const IntRect* skip) {
  }
}

static inline bool clear_requires_scissor(Texture& t) {
  return ctx->scissortest && !ctx->scissor.contains(t.offset_bounds());
}

// Setup a clear on a texture. This may either force an immediate clear or
// potentially punt to a delayed clear, if applicable.
template <typename T>
static void request_clear(Texture& t, int layer, T value) {
static void request_clear(Texture& t, int layer, T value,
                          const IntRect& scissor) {
  // If the clear would require a scissor, force clear anything outside
  // the scissor, and then immediately clear anything inside the scissor.
  if (clear_requires_scissor(t)) {
    IntRect skip = ctx->scissor - t.offset;
  if (!scissor.contains(t.offset_bounds())) {
    IntRect skip = scissor - t.offset;
    force_clear<T>(t, &skip);
    clear_buffer<T>(t, value, layer);
    clear_buffer<T>(t, value, layer, skip.intersection(t.bounds()));
  } else if (t.depth > 1) {
    // Delayed clear is not supported on texture arrays.
    t.disable_delayed_clear();
    clear_buffer<T>(t, value, layer);
    clear_buffer<T>(t, value, layer, t.bounds());
  } else {
    // Do delayed clear for 2D texture without scissor.
    t.enable_delayed_clear(value);
  }
}

template <typename T>
static inline void request_clear(Texture& t, int layer, T value) {
  // If scissoring is enabled, use the scissor rect. Otherwise, just scissor to
  // the entire texture bounds.
  request_clear(t, layer, value,
                ctx->scissortest ? ctx->scissor : t.offset_bounds());
}

// Initialize a depth texture by setting the first run in each row to encompass
// the entire row.
void Texture::init_depth_runs(uint16_t depth) {
@@ -2509,10 +2513,10 @@ static ALWAYS_INLINE void fill_depth_run(DepthRun* dst, size_t n,
}

// Fills a scissored region of a depth texture with a given depth.
void Texture::fill_depth_runs(uint16_t depth) {
void Texture::fill_depth_runs(uint16_t depth, const IntRect& scissor) {
  if (!buf) return;
  assert(cleared());
  IntRect bb = ctx->apply_scissor(*this);
  IntRect bb = bounds().intersection(scissor - offset);
  DepthRun* runs = (DepthRun*)sample_ptr(0, bb.y0);
  for (int rows = bb.height(); rows > 0; rows--) {
    if (bb.width() >= width) {
@@ -2587,40 +2591,180 @@ GLenum CheckFramebufferStatus(GLenum target) {
  return GL_FRAMEBUFFER_COMPLETE;
}

void Clear(GLbitfield mask) {
  Framebuffer& fb = *get_framebuffer(GL_DRAW_FRAMEBUFFER);
  if ((mask & GL_COLOR_BUFFER_BIT) && fb.color_attachment) {
    Texture& t = ctx->textures[fb.color_attachment];
void ClearTexSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset,
                      GLint zoffset, GLsizei width, GLsizei height,
                      GLsizei depth, GLenum format, GLenum type,
                      const void* data) {
  if (level != 0) {
    assert(false);
    return;
  }
  Texture& t = ctx->textures[texture];
  assert(!t.locked);
    if (t.internal_format == GL_RGBA8) {
      uint32_t color = ctx->clearcolor;
      request_clear<uint32_t>(t, fb.layer, color);
    } else if (t.internal_format == GL_R8) {
      uint8_t color = uint8_t((ctx->clearcolor >> 16) & 0xFF);
      request_clear<uint8_t>(t, fb.layer, color);
    } else if (t.internal_format == GL_RG8) {
      uint16_t color = uint16_t((ctx->clearcolor & 0xFF00) |
                                ((ctx->clearcolor >> 16) & 0xFF));
      request_clear<uint16_t>(t, fb.layer, color);
    } else {
  if (zoffset < 0) {
    depth += zoffset;
    zoffset = 0;
  }
  if (zoffset + depth > max(t.depth, 1)) {
    depth = max(t.depth, 1) - zoffset;
  }
  if (width <= 0 || height <= 0 || depth <= 0) {
    return;
  }
  IntRect scissor = {xoffset, yoffset, xoffset + width, yoffset + height};
  if (t.internal_format == GL_DEPTH_COMPONENT16) {
    uint16_t value = 0xFFFF;
    switch (format) {
      case GL_DEPTH_COMPONENT:
        switch (type) {
          case GL_DOUBLE:
            value = uint16_t(*(const GLdouble*)data * 0xFFFF);
            break;
          case GL_FLOAT:
            value = uint16_t(*(const GLfloat*)data * 0xFFFF);
            break;
          case GL_UNSIGNED_SHORT:
            value = uint16_t(*(const GLushort*)data);
            break;
          default:
            assert(false);
            break;
        }
        break;
      default:
        assert(false);
        break;
    }
  if ((mask & GL_DEPTH_BUFFER_BIT) && fb.depth_attachment) {
    Texture& t = ctx->textures[fb.depth_attachment];
    assert(t.internal_format == GL_DEPTH_COMPONENT16);
    uint16_t depth = uint16_t(0xFFFF * ctx->cleardepth);
    if (t.cleared() && clear_requires_scissor(t)) {
    assert(zoffset == 0 && depth == 1);
    if (t.cleared() && !scissor.contains(t.offset_bounds())) {
      // If we need to scissor the clear and the depth buffer was already
      // initialized, then just fill runs for that scissor area.
      t.fill_depth_runs(depth);
      t.fill_depth_runs(value, scissor);
    } else {
      // Otherwise, the buffer is either uninitialized or the clear would
      // encompass the entire buffer. If uninitialized, we can safely fill
      // the entire buffer with any value and thus ignore any scissoring.
      t.init_depth_runs(depth);
      t.init_depth_runs(value);
    }
    return;
  }

  uint32_t color = 0xFF000000;
  switch (type) {
    case GL_FLOAT: {
      const GLfloat* f = (const GLfloat*)data;
      Float v = {0.0f, 0.0f, 0.0f, 1.0f};
      switch (format) {
        case GL_RGBA:
          v.w = f[3];  // alpha
          FALLTHROUGH;
        case GL_RGB:
          v.z = f[2];  // blue
          FALLTHROUGH;
        case GL_RG:
          v.y = f[1];  // green
          FALLTHROUGH;
        case GL_RED:
          v.x = f[0];  // red
          break;
        default:
          assert(false);
          break;
      }
      color = bit_cast<uint32_t>(CONVERT(round_pixel(v), U8));
      break;
    }
    case GL_UNSIGNED_BYTE: {
      const GLubyte* b = (const GLubyte*)data;
      switch (format) {
        case GL_RGBA:
          color = (color & ~0xFF000000) | (uint32_t(b[3]) << 24);  // alpha
          FALLTHROUGH;
        case GL_RGB:
          color = (color & ~0x00FF0000) | (uint32_t(b[2]) << 16);  // blue
          FALLTHROUGH;
        case GL_RG:
          color = (color & ~0x0000FF00) | (uint32_t(b[1]) << 8);  // green
          FALLTHROUGH;
        case GL_RED:
          color = (color & ~0x000000FF) | uint32_t(b[0]);  // red
          break;
        default:
          assert(false);
          break;
      }
      break;
    }
    default:
      assert(false);
      break;
  }

  for (int layer = zoffset; layer < zoffset + depth; layer++) {
    switch (t.internal_format) {
      case GL_RGBA8:
        // Clear color needs to swizzle to BGRA.
        request_clear<uint32_t>(t, layer,
                                (color & 0xFF00FF00) |
                                    ((color << 16) & 0xFF0000) |
                                    ((color >> 16) & 0xFF),
                                scissor);
        break;
      case GL_R8:
        request_clear<uint8_t>(t, layer, uint8_t(color & 0xFF), scissor);
        break;
      case GL_RG8:
        request_clear<uint16_t>(t, layer, uint16_t(color & 0xFFFF), scissor);
        break;
      default:
        assert(false);
        break;
    }
  }
}

void ClearTexImage(GLuint texture, GLint level, GLenum format, GLenum type,
                   const void* data) {
  Texture& t = ctx->textures[texture];
  IntRect scissor = t.offset_bounds();
  ClearTexSubImage(texture, level, scissor.x0, scissor.y0, 0, scissor.width(),
                   scissor.height(), max(t.depth, 1), format, type, data);
}

void Clear(GLbitfield mask) {
  Framebuffer& fb = *get_framebuffer(GL_DRAW_FRAMEBUFFER);
  if ((mask & GL_COLOR_BUFFER_BIT) && fb.color_attachment) {
    Texture& t = ctx->textures[fb.color_attachment];
    IntRect scissor = ctx->scissortest
                          ? ctx->scissor.intersection(t.offset_bounds())
                          : t.offset_bounds();
    ClearTexSubImage(fb.color_attachment, 0, scissor.x0, scissor.y0, fb.layer,
                     scissor.width(), scissor.height(), 1, GL_RGBA, GL_FLOAT,
                     ctx->clearcolor);
  }
  if ((mask & GL_DEPTH_BUFFER_BIT) && fb.depth_attachment) {
    Texture& t = ctx->textures[fb.depth_attachment];
    IntRect scissor = ctx->scissortest
                          ? ctx->scissor.intersection(t.offset_bounds())
                          : t.offset_bounds();
    ClearTexSubImage(fb.depth_attachment, 0, scissor.x0, scissor.y0, 0,
                     scissor.width(), scissor.height(), 1, GL_DEPTH_COMPONENT,
                     GL_DOUBLE, &ctx->cleardepth);
  }
}

void ClearColorRect(GLuint fbo, GLint xoffset, GLint yoffset, GLsizei width,
                    GLsizei height, GLfloat r, GLfloat g, GLfloat b,
                    GLfloat a) {
  GLfloat color[] = {r, g, b, a};
  Framebuffer& fb = ctx->framebuffers[fbo];
  Texture& t = ctx->textures[fb.color_attachment];
  IntRect scissor =
      IntRect{xoffset, yoffset, xoffset + width, yoffset + height}.intersection(
          t.offset_bounds());
  ClearTexSubImage(fb.color_attachment, 0, scissor.x0, scissor.y0, fb.layer,
                   scissor.width(), scissor.height(), 1, GL_RGBA, GL_FLOAT,
                   color);
}

void InvalidateFramebuffer(GLenum target, GLsizei num_attachments,
+1 −0
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ typedef intptr_t GLintptr;
#define GL_INT 0x1404
#define GL_UNSIGNED_INT 0x1405
#define GL_FLOAT 0x1406
#define GL_DOUBLE 0x1408

#define GL_RED 0x1903
#define GL_GREEN 0x1904
+65 −33
Original line number Diff line number Diff line
@@ -15,8 +15,11 @@ macro_rules! debug {
}

#[repr(C)]
struct LockedTexture { _private: [u8; 0] }
struct LockedTexture {
    _private: [u8; 0],
}

#[allow(dead_code)]
extern "C" {
    fn ActiveTexture(texture: GLenum);
    fn BindTexture(target: GLenum, texture: GLuint);
@@ -62,11 +65,7 @@ extern "C" {
        level: GLint,
    );
    fn CheckFramebufferStatus(target: GLenum) -> GLenum;
    fn InvalidateFramebuffer(
        target: GLenum,
        num_attachments: GLsizei,
        attachments: *const GLenum,
    );
    fn InvalidateFramebuffer(target: GLenum, num_attachments: GLsizei, attachments: *const GLenum);
    fn TexStorage3D(
        target: GLenum,
        levels: GLint,
@@ -186,6 +185,31 @@ extern "C" {
    fn ClearColor(r: GLfloat, g: GLfloat, b: GLfloat, a: GLfloat);
    fn ClearDepth(depth: GLdouble);
    fn Clear(mask: GLbitfield);
    fn ClearTexSubImage(
        target: GLenum,
        level: GLint,
        xoffset: GLint,
        yoffset: GLint,
        zoffset: GLint,
        width: GLsizei,
        height: GLsizei,
        depth: GLsizei,
        format: GLenum,
        ty: GLenum,
        data: *const c_void,
    );
    fn ClearTexImage(target: GLenum, level: GLint, format: GLenum, ty: GLenum, data: *const c_void);
    fn ClearColorRect(
        fbo: GLuint,
        xoffset: GLint,
        yoffset: GLint,
        width: GLsizei,
        height: GLsizei,
        r: GLfloat,
        g: GLfloat,
        b: GLfloat,
        a: GLfloat,
    );
    fn PixelStorei(name: GLenum, param: GLint);
    fn ReadPixels(
        x: GLint,
@@ -389,11 +413,34 @@ impl Context {
            let mut width: i32 = 0;
            let mut height: i32 = 0;
            let mut stride: i32 = 0;
            let data_ptr = GetColorBuffer(fbo, flush as GLboolean, &mut width, &mut height, &mut stride);
            let data_ptr = GetColorBuffer(
                fbo,
                flush as GLboolean,
                &mut width,
                &mut height,
                &mut stride,
            );
            (data_ptr, width, height, stride)
        }
    }

    pub fn clear_color_rect(
        &self,
        fbo: GLuint,
        xoffset: GLint,
        yoffset: GLint,
        width: GLsizei,
        height: GLsizei,
        r: f32,
        g: f32,
        b: f32,
        a: f32,
    ) {
        unsafe {
            ClearColorRect(fbo, xoffset, yoffset, width, height, r, g, b, a);
        }
    }

    pub fn set_texture_buffer(
        &self,
        tex: GLuint,
@@ -1548,13 +1595,7 @@ impl Gl for Context {

    fn draw_arrays(&self, mode: GLenum, first: GLint, count: GLsizei) {
        unsafe {
            DrawElementsInstanced(
                mode,
                count,
                NONE,
                first as GLintptr,
                1,
            );
            DrawElementsInstanced(mode, count, NONE, first as GLintptr, 1);
        }
    }

@@ -1566,13 +1607,7 @@ impl Gl for Context {
        primcount: GLsizei,
    ) {
        unsafe {
            DrawElementsInstanced(
                mode,
                count,
                NONE,
                first as GLintptr,
                primcount,
            );
            DrawElementsInstanced(mode, count, NONE, first as GLintptr, primcount);
        }
    }

@@ -1589,13 +1624,7 @@ impl Gl for Context {
        );
        //panic!();
        unsafe {
            DrawElementsInstanced(
                mode,
                count,
                element_type,
                indices_offset as GLintptr,
                1,
            );
            DrawElementsInstanced(mode, count, element_type, indices_offset as GLintptr, 1);
        }
    }

@@ -2477,14 +2506,17 @@ impl LockedResource {

impl Clone for LockedResource {
    fn clone(&self) -> Self {
        unsafe { LockResource(self.0); }
        unsafe {
            LockResource(self.0);
        }
        LockedResource(self.0)
    }
}

impl Drop for LockedResource {
    fn drop(&mut self) {
        unsafe { UnlockResource(self.0); }
        unsafe {
            UnlockResource(self.0);
        }
    }
}